├── .gitattributes ├── .gitignore ├── .travis.yml ├── HELP.txt ├── LICENSE ├── Makefile ├── README.md ├── android ├── adb.go ├── const.go ├── const_windows.go ├── fastboot.go └── tool.go ├── devices.go ├── devices.toml ├── main.go ├── prebuilts ├── linux │ ├── NOTICE.txt │ ├── adb │ ├── fastboot │ └── uninstall.sh ├── mac │ ├── NOTICE.txt │ ├── adb │ └── fastboot └── windows │ ├── AdbWinApi.dll │ ├── AdbWinUsbApi.dll │ ├── NOTICE.txt │ ├── adb.exe │ ├── fastboot.exe │ └── uninstall.cmd ├── remote └── download.go ├── strings.go ├── tests ├── functional.sh └── test.sh └── version.go /.gitattributes: -------------------------------------------------------------------------------- 1 | # make sure windows scripts use CRLF endings 2 | *.cmd text eol=crlf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /.vscode 3 | /install 4 | /install.exe 5 | /debug 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 1.8 4 | 5 | install: 6 | - go get -v ./... 7 | 8 | script: 9 | - make 10 | - make tests 11 | -------------------------------------------------------------------------------- /HELP.txt: -------------------------------------------------------------------------------- 1 | ,--,--,--. ,--,--.,--.--.,--.,--. 2 | | |' ,-. || .--'| || | 3 | | | | |\ '-' || | ' '' ' 4 | `--`--`--' `--`--'`--' `----' 5 | 6 | PLEASE READ BEFORE INSTALLING 7 | ============================= 8 | 9 | Before deciding to install the Maru beta on your device, please be aware of the 10 | following: 11 | 12 | **Installing Maru will wipe all of your personal data so please back up anything 13 | important!** 14 | 15 | You can back up important stuff like photos/videos/text messages/call logs 16 | somewhere off of your device, like your PC or the cloud. There are several apps 17 | available from the Play store that can help you with this--just search "backup". 18 | 19 | If you use a custom recovery that supports backups like TWRP, make sure to take 20 | a complete device backup before installing Maru using the update zip. 21 | 22 | **DISCLAIMER**: The Maru OS Project authors will not be held responsible for any 23 | loss of data or damage to your device. Keep in mind that installing aftermarket 24 | firmware may void your device's warranty. 25 | 26 | IF YOU ALREADY HAVE A CUSTOM RECOVERY 27 | ------------------------------------- 28 | 29 | Maru OS now has a standard update zip that you can flash with TWRP just 30 | like any other ROM--use that instead of this installer! 31 | 32 | 33 | INSTALLING 34 | ========== 35 | 36 | Installing Maru on your device is easy! 37 | 38 | Just run the interactive installer and follow along. 39 | 40 | Mac 41 | --- 42 | 43 | Navigate to the unzipped installer folder, right-click (Control + click) "install", 44 | and select Open. 45 | 46 | Tip: Depending on your security settings, you may get a dialog that says 47 | "install is from an unidentified developer. Are you sure you want to open it?". 48 | Please select Open. See https://support.apple.com/kb/PH21769?locale=en_US for 49 | more details. We promise we're not malware :) 50 | 51 | 52 | Linux 53 | ----- 54 | 55 | Open a terminal, cd to the unzipped installer, and run the installer with: 56 | 57 | $ ./install 58 | 59 | 60 | Windows 61 | ------- 62 | 63 | First, install the Google USB Driver to communicate with Nexus devices: 64 | 65 | 1. Enable USB Debugging on your device (see HOW TO ENABLE USB DEBUGGING below) 66 | 67 | 2. Download the driver from http://developer.android.com/sdk/win-usb.html 68 | 69 | 3. Install the driver following the instructions on 70 | http://developer.android.com/tools/extras/oem-usb.html#InstallingDriver 71 | 72 | NOTE: Step 3 for Windows 7 says "Select 'Devices' in the left pane". If you 73 | don't see 'Devices', look instead for 'Device Manager'. 74 | 75 | Then navigate to the unzipped installer folder and double-click "install". 76 | 77 | Tip: We have found that installing the Google USB Driver can be very confusing. 78 | You may need to select the driver for your device multiple times before 79 | Windows finally installs it correctly. You may also need to wait while Windows 80 | installs the driver a second time after your device boots into recovery mode. 81 | 82 | 83 | UNINSTALLING / RESTORING TO FACTORY 84 | =================================== 85 | 86 | If you've installed Maru and decided you want to get your device back to 87 | the way it was, you can easily re-install your factory image with our 88 | interactive uninstaller. 89 | 90 | Grab your device's factory image for the Android version you would like from 91 | https://developers.google.com/android/images and follow along. 92 | 93 | NOTE: You will have to enable USB Debugging again even if you already did so for 94 | installing Maru. (You must be a pro at this by now!) 95 | 96 | Mac 97 | --- 98 | 99 | Navigate to the unzipped installer folder, right-click (Control + click) "uninstall", 100 | and select Open. 101 | 102 | Tip: Depending on your security settings, you may get a dialog that says 103 | "uninstall is from an unidentified developer. Are you sure you want to open it?". 104 | Please select Open. See https://support.apple.com/kb/PH21769?locale=en_US for 105 | more details. We promise we're not malware :) 106 | 107 | 108 | Linux 109 | ----- 110 | 111 | Open a terminal, cd to the unzipped installer, and run the "uninstall.sh" script with: 112 | 113 | $ ./uninstall.sh 114 | 115 | 116 | Windows 117 | ------- 118 | 119 | Navigate to the unzipped installer folder and double-click "uninstall". 120 | 121 | 122 | You should now have a fresh factory installation on your device. 123 | 124 | 125 | RESTORING GOOGLE APPS 126 | ===================== 127 | 128 | If you would like to restore access to the Play store for your device, you will 129 | need to do a little extra work. 130 | 131 | We have had success using the TWRP custom recovery to restore Google apps: 132 | 133 | 1. Download Play apps from a 3rd party packager like Open Gapps and save it in 134 | the same directory you unzipped the Maru installer: 135 | 136 | http://opengapps.org/?arch=arm&api=6.0&variant=pico 137 | 138 | 2. Download the latest TWRP for your device and save it in the same directory 139 | you unzipped the Maru installer: 140 | 141 | https://twrp.me/Devices/ 142 | 143 | 3. Connect your device to your computer, enable USB Debugging, 144 | and follow the instructions below depending on your platform: 145 | 146 | (Make sure to substitute the correct name of the file you downloaded for anything in brackets) 147 | 148 | Mac or Linux 149 | ------------ 150 | 151 | Open up a terminal and run the following: 152 | 153 | $ cd 154 | $ ./adb push -p .zip /sdcard/ 155 | $ ./adb reboot bootloader 156 | $ ./fastboot flash recovery .img 157 | 158 | Windows 159 | ------- 160 | 161 | Open up Command Prompt and run the following: 162 | 163 | $ cd 164 | $ adb push -p .zip /sdcard/ 165 | $ adb reboot bootloader 166 | $ fastboot flash recovery .img 167 | 168 | 169 | You will now be in the bootloader and need to do a few more steps: 170 | 171 | 4. Use the button on your device to cycle through the bootloader options 172 | to "Recovery mode" and hit the button to boot into TWRP recovery 173 | 174 | (You will be greeted by a screen that allows you to "Keep Read Only" or "Swipe to Allow 175 | Modifications". It's safest to tap "Keep Read Only": TWRP will be replaced by your stock 176 | recovery after your phone reboots. Alternatively, if you would like to keep TWRP recovery 177 | on your device after your phone reboots, swipe right.) 178 | 179 | 5. Tap "Install" 180 | 181 | 6. Tap the Google apps zip file that you downloaded earlier (you may need to scroll down) 182 | 183 | 7. Swipe to confirm flash of the Google apps 184 | 185 | 8. Tap "Reboot System" to reboot back into Maru 186 | 187 | (You may be asked to install SuperSU to root your device. If you know what rooting your 188 | device means and want to have it rooted then go ahead. Otherwise, it's best to tap 189 | "Do Not Install".) 190 | 191 | You should now have Google Play apps! 192 | 193 | 194 | HOW TO ENABLE USB DEBUGGING 195 | =========================== 196 | 197 | You'll see instructions to enable USB Debugging as the first step before 198 | running our installer or uninstaller. Enabling USB Debugging is important 199 | because it lets the Maru installer talk to your device. 200 | 201 | 1. Go to the Settings app and scroll down to the System section 202 | 203 | NOTE: If you already have "Developer options" 204 | under System then go directly to #5 205 | 206 | 2. Tap on "About phone" 207 | 3. Tap "Build number" 7 times until you get a message 208 | that says you are now a developer 209 | 4. Go back to the Settings app and scroll down to the System section again 210 | 5. Tap on "Developer options" 211 | 6. Ensure that "USB debugging" is enabled 212 | 7. Tap "OK" if you see a dialog asking you to allow USB Debugging for 213 | your computer's RSA key fingerprint (you may have to do this multiple 214 | times unless you check "Always allow from this computer") 215 | 216 | Remember that you will have to enable USB Debugging again whenever your 217 | device factory resets (after unlocking your bootloader for example) or 218 | completes a fresh install. 219 | 220 | 221 | ISSUES 222 | ====== 223 | 224 | Please file any bugs or feature requests as issues on the central Maru OS 225 | repository: 226 | 227 | https://github.com/maruos/maruos 228 | 229 | You can also find help by explaining your issue in the Maru OS forum: 230 | 231 | https://groups.google.com/forum/#!forum/maru-os 232 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2015-2016 Preetam J. D'Souza 180 | Copyright 2016 The Maru OS Project 181 | 182 | Licensed under the Apache License, Version 2.0 (the "License"); 183 | you may not use this file except in compliance with the License. 184 | You may obtain a copy of the License at 185 | 186 | http://www.apache.org/licenses/LICENSE-2.0 187 | 188 | Unless required by applicable law or agreed to in writing, software 189 | distributed under the License is distributed on an "AS IS" BASIS, 190 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 191 | See the License for the specific language governing permissions and 192 | limitations under the License. 193 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017 The Maru OS Project 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | BINARY=install 17 | VERSION=$(shell git describe --always --dirty) 18 | LDFLAGS=-ldflags "-X main.Version=$(VERSION)" 19 | 20 | DIST_DIR=dist 21 | ZIP_PREFIX=$(DIST_DIR)/nethunter-installer-$(VERSION) 22 | ZIP_ASSETS=HELP.txt devices.toml 23 | ZIP_FLAGS=-X --junk-paths 24 | 25 | all: linux darwin windows default 26 | 27 | default: 28 | go build $(LDFLAGS) -o $(BINARY) 29 | 30 | $(DIST_DIR): 31 | mkdir -p $(DIST_DIR) 32 | 33 | linux: $(DIST_DIR) 34 | GOOS=$@ GOARCH=amd64 GOARM=7 go build $(LDFLAGS) -o $(BINARY) 35 | zip $(ZIP_FLAGS) $(ZIP_PREFIX)-$@.zip $(BINARY) prebuilts/$@/* $(ZIP_ASSETS) 36 | 37 | darwin: $(DIST_DIR) 38 | GOOS=$@ GOARCH=amd64 go build $(LDFLAGS) -o $(BINARY) 39 | cp prebuilts/linux/uninstall.sh prebuilts/mac/uninstall 40 | zip $(ZIP_FLAGS) $(ZIP_PREFIX)-$@.zip $(BINARY) prebuilts/mac/* $(ZIP_ASSETS) 41 | rm prebuilts/mac/uninstall 42 | 43 | windows: $(DIST_DIR) 44 | GOOS=$@ GOARCH=amd64 go build $(LDFLAGS) -o $(BINARY).exe 45 | zip $(ZIP_FLAGS) $(ZIP_PREFIX)-$@.zip $(BINARY).exe prebuilts/$@/* $(ZIP_ASSETS) 46 | 47 | tests: default 48 | ./tests/functional.sh 49 | 50 | clean: 51 | -if [ -f $(BINARY) ] ; then rm $(BINARY); fi 52 | -if [ -f $(BINARY).exe ] ; then rm $(BINARY).exe; fi 53 | -if [ -d $(DIST_DIR) ] ; then rm -r $(DIST_DIR); fi 54 | 55 | .PHONY: all default linux darwin windows tests 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Maru OS Installers 2 | 3 | [![Build Status](https://travis-ci.org/maruos/installer.svg?branch=master)](https://travis-ci.org/maruos/installer) 4 | 5 | Easy installers for Maru OS. 6 | 7 | ### Contributing 8 | 9 | See the [main Maru OS repository](https://github.com/maruos/maruos) for more 10 | info. 11 | 12 | ### Licensing 13 | 14 | This repository is licensed under the Apache License, Version 2.0. See 15 | [LICENSE](LICENSE) for the details. 16 | -------------------------------------------------------------------------------- /android/adb.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | // Wrapper around an adb binary. 18 | 19 | package android 20 | 21 | import ( 22 | "strings" 23 | ) 24 | 25 | type AdbError struct { 26 | Output string 27 | Err error 28 | } 29 | 30 | func (e *AdbError) Error() string { 31 | return "adb: " + e.Err.Error() 32 | } 33 | 34 | func NewAdbError(output string, err error) *AdbError { 35 | return &AdbError{output, err} 36 | } 37 | 38 | type AdbClient struct { 39 | BinaryAndroidTool 40 | } 41 | 42 | func NewAdbClient() AdbClient { 43 | return AdbClient{BinaryAndroidTool{"adb"}} 44 | } 45 | 46 | func (a *AdbClient) Status() (AndroidDeviceStatus, error) { 47 | output, err := a.Run("devices") 48 | if err != nil { 49 | return NoDeviceFound, NewAdbError(output, err) 50 | } 51 | 52 | lines := strings.Split(output, AdbLineSeperator) 53 | 54 | // first line is always "List of devices attached" so we examine the second 55 | // line 56 | if len(lines[1]) == 0 { 57 | return NoDeviceFound, nil 58 | } else if strings.Contains(output, "no permissions") { 59 | return NoUsbPerms, nil 60 | } else if strings.Contains(output, "unauthorized") { 61 | return DeviceUnauthorized, nil 62 | } else { 63 | return DeviceConnected, nil 64 | } 65 | } 66 | 67 | func (a *AdbClient) PushFg(local, remote string) (err error) { 68 | err = a.RunFg("push", "-p", local, remote) 69 | if err != nil { 70 | return NewAdbError("", err) 71 | } 72 | return err 73 | } 74 | 75 | func (a *AdbClient) Reboot(image string) (err error) { 76 | output, err := a.Run("reboot", image) 77 | if err != nil { 78 | return NewAdbError(output, err) 79 | } 80 | return err 81 | } 82 | 83 | func (a *AdbClient) Sideload(zip string) (err error) { 84 | output, err := a.Run("sideload", zip) 85 | if err != nil { 86 | return NewAdbError(output, err) 87 | } 88 | return err 89 | } 90 | 91 | func (a *AdbClient) Shell(cmd string) (err error) { 92 | output, err := a.Run("shell", cmd) 93 | if err != nil { 94 | return NewAdbError(output, err) 95 | } 96 | return err 97 | } 98 | -------------------------------------------------------------------------------- /android/const.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | // +build linux darwin 18 | 19 | package android 20 | 21 | const ( 22 | AdbLineSeperator = "\n" 23 | FastbootLineSeperator = "\n" 24 | ) 25 | -------------------------------------------------------------------------------- /android/const_windows.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package android 18 | 19 | const ( 20 | AdbLineSeperator = "\r\n" 21 | FastbootLineSeperator = "\r\n" 22 | ) 23 | -------------------------------------------------------------------------------- /android/fastboot.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | // Wrapper around a fastboot binary. 18 | // 19 | // Note that fastboot (strangely) writes only to stderr for some actions like 20 | // "getvar" or "oem device-info". 21 | 22 | package android 23 | 24 | import ( 25 | "strings" 26 | ) 27 | 28 | type FastbootError struct { 29 | Output string 30 | Err error 31 | } 32 | 33 | func (e *FastbootError) Error() string { 34 | return "fastboot: " + e.Err.Error() 35 | } 36 | 37 | func NewFastbootError(output string, err error) *FastbootError { 38 | return &FastbootError{output, err} 39 | } 40 | 41 | type FastbootClient struct { 42 | BinaryAndroidTool 43 | } 44 | 45 | func NewFastbootClient() FastbootClient { 46 | return FastbootClient{BinaryAndroidTool{"fastboot"}} 47 | } 48 | 49 | func (f *FastbootClient) getVar(variable string) (value string, err error) { 50 | output, err := f.Run("getvar", variable) 51 | if err != nil { 52 | return output, NewFastbootError(output, err) 53 | } 54 | 55 | // fastboot reports "[variable]: [value]\n...\n" 56 | value = strings.Split(strings.Split(output, FastbootLineSeperator)[0], " ")[1] 57 | return value, nil 58 | } 59 | 60 | func (f *FastbootClient) Status() (AndroidDeviceStatus, error) { 61 | output, err := f.Run("devices") 62 | if err != nil { 63 | return NoDeviceFound, NewFastbootError(output, err) 64 | } 65 | 66 | if len(output) == 0 { 67 | return NoDeviceFound, nil 68 | } else if strings.Contains(output, "no permissions") { 69 | return NoUsbPerms, nil 70 | } else { 71 | return DeviceConnected, nil 72 | } 73 | } 74 | 75 | func (f *FastbootClient) GetProduct() (product string, err error) { 76 | return f.getVar("product") 77 | } 78 | 79 | func (f *FastbootClient) FlashRecovery(image string) (err error) { 80 | output, err := f.Run("flash", "recovery", image) 81 | if err != nil { 82 | return NewFastbootError(output, err) 83 | } 84 | return nil 85 | } 86 | 87 | func (f *FastbootClient) Boot(image string) (err error) { 88 | output, err := f.Run("boot", image) 89 | if err != nil { 90 | return NewFastbootError(output, err) 91 | } 92 | return nil 93 | } 94 | 95 | func (f *FastbootClient) Reboot() (err error) { 96 | output, err := f.Run("reboot") 97 | if err != nil { 98 | return NewFastbootError(output, err) 99 | } 100 | return nil 101 | } 102 | 103 | func (f *FastbootClient) Unlocked() (bool, error) { 104 | product, err := f.GetProduct() 105 | if err != nil { 106 | return false, err 107 | } 108 | 109 | // flo is a special case since it reports the wrong lock state from oem 110 | // device-info 111 | if "flo" == product { 112 | lockState, err := f.getVar("lock_state") 113 | return "unlocked" == lockState, err 114 | } 115 | 116 | deviceInfo, err := f.Run("oem", "device-info") 117 | if err != nil { 118 | return false, err 119 | } 120 | 121 | unlocked := false 122 | lines := strings.Split(deviceInfo, FastbootLineSeperator) 123 | for _, line := range lines { 124 | if strings.Contains(line, "Device unlocked") { 125 | unlocked = "true" == strings.Split(line, " ")[3] 126 | } 127 | } 128 | 129 | return unlocked, err 130 | } 131 | 132 | func (f *FastbootClient) Unlock() (err error) { 133 | output, err := f.Run("oem", "unlock") 134 | if err != nil { 135 | return NewFastbootError(output, err) 136 | } 137 | return nil 138 | } 139 | -------------------------------------------------------------------------------- /android/tool.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package android 18 | 19 | import ( 20 | "os" 21 | "os/exec" 22 | ) 23 | 24 | // AndroidDeviceTool represents a program for interacting with Android devices. 25 | type AndroidDeviceTool interface { 26 | DeviceConnected() bool 27 | } 28 | 29 | // AndroidDeviceStatus represents the main device states that are relevant to 30 | // an AndroidDeviceTool. 31 | type AndroidDeviceStatus uint8 32 | 33 | const ( 34 | NoDeviceFound AndroidDeviceStatus = iota 35 | NoUsbPerms 36 | DeviceUnauthorized 37 | DeviceConnected 38 | ) 39 | 40 | // BinaryAndroidTool represents an AndroidDeviceTool that is run as a binary program. 41 | type BinaryAndroidTool struct { 42 | Name string 43 | } 44 | 45 | func (b *BinaryAndroidTool) Run(args ...string) (string, error) { 46 | out, err := exec.Command(b.Name, args...).CombinedOutput() 47 | return string(out), err 48 | } 49 | 50 | func (b *BinaryAndroidTool) RunFg(args ...string) error { 51 | cmd := exec.Command(b.Name, args...) 52 | cmd.Stdout = os.Stdout 53 | cmd.Stderr = os.Stderr 54 | return cmd.Run() 55 | } 56 | -------------------------------------------------------------------------------- /devices.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package main 18 | 19 | import ( 20 | "fmt" 21 | "io/ioutil" 22 | 23 | "github.com/BurntSushi/toml" 24 | ) 25 | 26 | type device struct { 27 | Common_name string 28 | Product_name string 29 | 30 | Nhos_file string 31 | Nhos_url string 32 | 33 | Nhfs_file string 34 | Nhfs_url string 35 | 36 | Gapps_file string 37 | Gapps_url string 38 | 39 | Twrp_file string 40 | Twrp_url string 41 | 42 | Extra_file string 43 | Extra_url string 44 | } 45 | 46 | type devices struct { 47 | Device []device 48 | } 49 | 50 | func readDevicesConfig() devices { 51 | b, err := ioutil.ReadFile("devices.toml") 52 | if err != nil { 53 | fmt.Print(err) 54 | } 55 | 56 | var nhConfig devices 57 | if _, err := toml.Decode(string(b), &nhConfig); err != nil { 58 | eEcho("ERROR READING TOML") 59 | } 60 | return nhConfig 61 | } 62 | 63 | func findDeviceConfig(nhDevices devices, deviceProductName string) device { 64 | for _, d := range nhDevices.Device { 65 | if d.Product_name == deviceProductName { 66 | return d 67 | } 68 | } 69 | return device{} 70 | } 71 | -------------------------------------------------------------------------------- /devices.toml: -------------------------------------------------------------------------------- 1 | # This is a TOML document for nethunter device configs. 2 | 3 | [[device]] 4 | 5 | common_name = "Nexus 5" 6 | product_name = "hammerhead" 7 | 8 | nhos_file = "lineage-14.1-20171008-UNOFFICIAL-hammerhead.zip" 9 | nhos_url = "https://build.nethunter.com/installer/nexus5/lineage-14.1-20171008-UNOFFICIAL-hammerhead.zip" 10 | 11 | nhfs_file = "update-nethunter-generic-armhf-20171007_215146.zip" 12 | nhfs_url = "https://build.nethunter.com/installer/generic/update-nethunter-generic-armhf-20171007_215146.zip" 13 | 14 | gapps_file = "open_gapps-arm-7.1-mini-20171007.zip" 15 | gapps_url = "https://build.nethunter.com/installer/gapps/open_gapps-arm-7.1-mini-20171007.zip" 16 | 17 | twrp_file = "twrp-3.1.1-0-hammerhead.img" 18 | twrp_url = "https://dl.twrp.me/hammerhead/twrp-3.1.1-0-hammerhead.img" 19 | 20 | [[device]] 21 | 22 | common_name = "Nexus 5X fixme" 23 | product_name = "bullhead" 24 | 25 | nhos_file = "BULLHEAD_FIXME.zip" 26 | nhos_url = "BULLHEAD_FIXME/misc/nexus5_installer/nethunter_hammerhead-ota-f91313a12f.zip" 27 | 28 | nhfs_file = "update-nethunter-generic-armhf-20171007_215146.zip" 29 | nhfs_url = "https://build.nethunter.com/installer/generic/update-nethunter-generic-armhf-20171007_215146.zip" 30 | 31 | gapps_file = "open_gapps-arm-7.1-mini-20171007.zip" 32 | gapps_url = "https://build.nethunter.com/installer/gapps/open_gapps-arm-7.1-mini-20171007.zip" 33 | 34 | twrp_file = "twrp-3.1.1-0-bullhead.img" 35 | twrp_url = "https://dl.twrp.me/bullhead/twrp-3.1.1-0-bullhead.img" 36 | 37 | [[device]] 38 | 39 | common_name = "OnePlus 1" 40 | product_name = "OnePlus 1" 41 | 42 | nhos_file = "lineage-14.1-20171009-UNOFFICIAL-bacon.zip" 43 | nhos_url = "https://build.nethunter.com/installer/oneplus1/lineage-14.1-20171009-UNOFFICIAL-bacon.zip" 44 | 45 | nhfs_file = "update-nethunter-generic-armhf-20171007_215146.zip" 46 | nhfs_url = "https://build.nethunter.com/installer/generic/update-nethunter-generic-armhf-20171007_215146.zip" 47 | 48 | gapps_file = "open_gapps-arm-7.1-mini-20171007.zip" 49 | gapps_url = "https://build.nethunter.com/installer/gapps/open_gapps-arm-7.1-mini-20171007.zip" 50 | 51 | twrp_file = "twrp-3.1.1-0-bullhead.img" 52 | twrp_url = "https://dl.twrp.me/bullhead/twrp-3.1.1-0-bullhead.img" 53 | 54 | [[device]] 55 | 56 | common_name = "OnePlus 5" 57 | product_name = "OnePlus 5" 58 | 59 | nhos_file = "lineage-14.1-20171008-UNOFFICIAL-cheeseburger.zip" 60 | nhos_url = "https://build.nethunter.com/installer/oneplus5/lineage-14.1-20171008-UNOFFICIAL-cheeseburger.zip" 61 | 62 | nhfs_file = "update-nethunter-generic-armhf-20171007_215146.zip" 63 | nhfs_url = "https://build.nethunter.com/installer/generic/update-nethunter-generic-armhf-20171007_215146.zip" 64 | 65 | gapps_file = "open_gapps-arm-7.1-mini-20171007.zip" 66 | gapps_url = "https://build.nethunter.com/installer/gapps/open_gapps-arm-7.1-mini-20171007.zip" 67 | 68 | twrp_file = "twrp-3.1.1-1-cheeseburger.img" 69 | twrp_url = "https://dl.twrp.me/cheeseburger/twrp-3.1.1-1-cheeseburger.img" 70 | 71 | extra_file = "oneplus_5_oxygenos_4.5.10_firmware.zip" 72 | extra_url = "https://build.nethunter.com/installer/oneplus5/oneplus_5_oxygenos_4.5.10_firmware.zip" -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package main 18 | 19 | import ( 20 | "bufio" 21 | "flag" 22 | "fmt" 23 | "log" 24 | "os" 25 | "path" 26 | "runtime" 27 | "time" 28 | 29 | "./android" 30 | "./remote" 31 | 32 | "github.com/dixonwille/wmenu" 33 | "github.com/pdsouza/toolbox.go/ui" 34 | ) 35 | 36 | const ( 37 | // Success exit codes. 38 | SuccessBase = 1<<5 + iota 39 | SuccessUserAbort 40 | SuccessBootloaderUnlocked 41 | 42 | Success = 0 43 | ) 44 | 45 | const ( 46 | // Error exit codes. 47 | ErrorBase = 1<<6 + iota 48 | ErrorPrereqs 49 | ErrorUserInput 50 | ErrorUsbPerms 51 | ErrorAdb 52 | ErrorFastboot 53 | ErrorRemote 54 | ErrorTWRP 55 | ) 56 | 57 | var ( 58 | reader = bufio.NewReader(os.Stdin) 59 | progressBar = ui.ProgressBar{0, 10, ""} 60 | ) 61 | 62 | func iEcho(format string, a ...interface{}) { 63 | fmt.Printf(format+"\n", a...) 64 | } 65 | 66 | func eEcho(msg string) { 67 | iEcho(msg) 68 | } 69 | 70 | func verifyAdbStatusOrAbort(adb *android.AdbClient) { 71 | status, err := adb.Status() 72 | if err != nil { 73 | eEcho("Failed to get adb status: " + err.Error()) 74 | exit(ErrorAdb) 75 | } 76 | if status == android.NoDeviceFound || status == android.DeviceUnauthorized { 77 | eEcho(MsgAdbIssue) 78 | exit(ErrorAdb) 79 | } else if status == android.NoUsbPerms { 80 | eEcho(MsgFixPerms) 81 | exit(ErrorUsbPerms) 82 | } 83 | } 84 | 85 | func verifyFastbootStatusOrAbort(fastboot *android.FastbootClient) { 86 | status, err := fastboot.Status() 87 | if err != nil { 88 | eEcho("Failed to get fastboot status: " + err.Error()) 89 | exit(ErrorFastboot) 90 | } 91 | if status == android.NoDeviceFound { 92 | eEcho(MsgFastbootNoDeviceFound) 93 | exit(ErrorFastboot) 94 | } else if status == android.NoUsbPerms { 95 | eEcho(MsgFixPerms) 96 | exit(ErrorUsbPerms) 97 | } 98 | } 99 | 100 | func progressCallback(percent float64) { 101 | progressBar.Progress = percent 102 | fmt.Print("\r" + progressBar.Render()) 103 | if percent == 1.0 { 104 | fmt.Println() 105 | } 106 | } 107 | 108 | func waitForOpKey(msg string) { 109 | fmt.Printf(msg) 110 | bufio.NewReader(os.Stdin).ReadBytes('\n') 111 | } 112 | 113 | func exit(code int) { 114 | // When run by double-clicking the executable on windows, the command 115 | // prompt will immediately exit upon program completion, making it hard for 116 | // users to see the last few messages. Let's explicitly wait for 117 | // acknowledgement from the user. 118 | if runtime.GOOS == "windows" { 119 | fmt.Print("\nPress [Enter] to exit...") 120 | reader.ReadLine() // pause until the user presses enter 121 | } 122 | 123 | os.Exit(code) 124 | } 125 | 126 | func main() { 127 | nhDevices := readDevicesConfig() 128 | 129 | /* 130 | Step 1 - Set path to binaries 131 | Step 2 - Verify ADB and Fastboot 132 | Step 3 - Check USB permissions 133 | Step 4 - Identify this is the correct device 134 | Step 5 - Detect if device is unlocked, then unlock 135 | Step 6 - Download: NethunterOS, TWRP Recovery, Kali Filesystem 136 | Step 7 - Boot into TWRP 137 | Step 8 - Install NH 138 | 139 | */ 140 | 141 | var versionFlag = flag.Bool("version", false, "print the program version") 142 | flag.Parse() 143 | if *versionFlag == true { 144 | iEcho("Nethunter installer version %s %s/%s", Version, runtime.GOOS, runtime.GOARCH) 145 | exit(Success) 146 | } 147 | 148 | myPath, err := os.Executable() 149 | if err != nil { 150 | panic(err) 151 | } 152 | 153 | // include any bundled binaries in PATH 154 | err = os.Setenv("PATH", path.Dir(myPath)+":"+os.Getenv("PATH")) 155 | if err != nil { 156 | eEcho("Failed to set PATH to include installer tools: " + err.Error()) 157 | exit(ErrorPrereqs) 158 | } 159 | 160 | // try to use the installer dir as the workdir to make sure any temporary 161 | // files or downloaded dependencies are isolated to the installer dir 162 | if err = os.Chdir(path.Dir(myPath)); err != nil { 163 | eEcho("Warning: failed to change working directory") 164 | } 165 | 166 | iEcho(MsgWelcome) 167 | // (We can remove this later) 168 | eEcho("The installer supports the following devices: ") 169 | for _, d := range nhDevices.Device { 170 | fmt.Printf(" - %s (%s)\n", d.Common_name, d.Product_name) 171 | } 172 | fmt.Print("\nAre you ready to install Nethunter? (yes/no): ") 173 | responseBytes, _, err := reader.ReadLine() 174 | if err != nil { 175 | iEcho("Failed to read input: ", err.Error()) 176 | exit(ErrorUserInput) 177 | } 178 | 179 | if "yes" != string(responseBytes) { 180 | iEcho("") 181 | iEcho("Aborting installation.") 182 | exit(SuccessUserAbort) 183 | } 184 | 185 | iEcho("") 186 | iEcho("Verifying installer tools...") 187 | adb := android.NewAdbClient() 188 | if _, err := adb.Status(); err != nil { 189 | eEcho("Failed to run adb: " + err.Error()) 190 | eEcho(MsgIncompleteZip) 191 | exit(ErrorPrereqs) 192 | } 193 | 194 | fastboot := android.NewFastbootClient() 195 | if _, err := fastboot.Status(); err != nil { 196 | eEcho("Failed to run fastboot: " + err.Error()) 197 | eEcho(MsgIncompleteZip) 198 | exit(ErrorPrereqs) 199 | } 200 | 201 | iEcho("Checking USB permissions...") 202 | status, _ := fastboot.Status() 203 | if status == android.NoDeviceFound { 204 | // We are in ADB mode (normal boot or recovery). 205 | 206 | verifyAdbStatusOrAbort(&adb) 207 | 208 | iEcho("Rebooting your device into bootloader...") 209 | err = adb.Reboot("bootloader") 210 | if err != nil { 211 | eEcho("Failed to reboot into bootloader: " + err.Error()) 212 | exit(ErrorAdb) 213 | } 214 | 215 | time.Sleep(7000 * time.Millisecond) 216 | 217 | if status, err = fastboot.Status(); err != nil || status == android.NoDeviceFound { 218 | eEcho("Failed to reboot device into bootloader!") 219 | exit(ErrorAdb) 220 | } 221 | } 222 | 223 | // We are in fastboot mode (the bootloader). 224 | 225 | verifyFastbootStatusOrAbort(&fastboot) 226 | 227 | iEcho("Identifying your device...") 228 | productName, err := fastboot.GetProduct() 229 | 230 | // OnePlus uses the same board name for every device. Need to let user select 231 | if productName == "QC_Reference_Phone" { 232 | menu := wmenu.NewMenu("Detected OnePlus device. Select which device: ") 233 | menu.Action(func(opts []wmenu.Opt) error { productName = opts[0].Text; return nil }) 234 | menu.Option("OnePlus 5", nil, true, nil) 235 | menu.Option("OnePlus 1", nil, false, nil) 236 | err := menu.Run() 237 | if err != nil { 238 | log.Fatal(err) 239 | } 240 | } 241 | 242 | if err != nil { 243 | eEcho("Failed to get device product info: " + err.Error()) 244 | exit(ErrorFastboot) 245 | } 246 | currDevice := findDeviceConfig(nhDevices, productName) 247 | 248 | // Check that we have the device config in the file 249 | 250 | if currDevice.Common_name != "" { 251 | fmt.Printf("Device and config found, using %s (%s) configuration and endpoints\n", currDevice.Common_name, currDevice.Product_name) 252 | } else { 253 | eEcho("Device config not found! Bye.") 254 | exit(1) 255 | } 256 | 257 | waitForOpKey("Press enter to continue with bootloader unlock check. Unlocking will wipe device if first time and will require restart.") // not sure about the sentence here 258 | 259 | unlocked, err := fastboot.Unlocked() 260 | if err != nil { 261 | iEcho("Warning: unable to determine bootloader lock state: " + err.Error()) 262 | } 263 | 264 | if !unlocked { 265 | iEcho("Unlocking bootloader, you will need to confirm this on your device...") 266 | err = fastboot.Unlock() 267 | if err != nil { 268 | eEcho("Failed to unlock bootloader: " + err.Error()) 269 | exit(ErrorFastboot) 270 | } 271 | fastboot.Reboot() 272 | iEcho(MsgUnlockSuccess) 273 | exit(SuccessBootloaderUnlocked) 274 | } 275 | 276 | // Check if there is any other extra files we need to get 277 | if currDevice.Extra_file != "" && currDevice.Extra_url != "" { 278 | if _, err := os.Stat(currDevice.Extra_file); os.IsNotExist(err) { // If file missing, download 279 | remote.DownloadURL(currDevice.Extra_url) 280 | } 281 | } 282 | 283 | // Request nethunter OS 284 | if _, err := os.Stat(currDevice.Nhos_file); os.IsNotExist(err) { // If file missing, download 285 | remote.DownloadURL(currDevice.Nhos_url) 286 | } 287 | 288 | // Request nethunter generic fileysstem 289 | if _, err := os.Stat(currDevice.Nhfs_file); os.IsNotExist(err) { // If file missing, download 290 | remote.DownloadURL(currDevice.Nhfs_url) 291 | } 292 | 293 | // Request gapps 294 | if _, err := os.Stat(currDevice.Gapps_file); os.IsNotExist(err) { // If file missing, download 295 | remote.DownloadURL(currDevice.Gapps_url) 296 | } 297 | 298 | // Download TWRP 299 | if _, err := os.Stat(currDevice.Twrp_file); os.IsNotExist(err) { // If file missing, download 300 | remote.DownloadURL(currDevice.Twrp_url) 301 | } 302 | 303 | waitForOpKey("Press enter to start the installation") 304 | 305 | // Flash TWRP recovery 306 | iEcho("Starting TWRP flash") 307 | err = fastboot.FlashRecovery(currDevice.Twrp_file) 308 | if err != nil { 309 | eEcho("Failed to flash TWRP Recovery: " + err.Error()) 310 | exit(ErrorTWRP) 311 | } 312 | 313 | // Boot into twrp 314 | iEcho("Booting TWRP to flash Nethunter update zip.\n Swipe to allow system modification in TWRP and wait") 315 | err = fastboot.Boot(currDevice.Twrp_file) 316 | if err != nil { 317 | eEcho("Failed to boot TWRP: " + err.Error()) 318 | exit(ErrorTWRP) 319 | } 320 | 321 | // Wait for TWRP 322 | waitForOpKey("Press enter when TWRP is fully loaded & ready") 323 | 324 | // Start fresh 325 | iEcho("Removing previous installations") 326 | time.Sleep(1000 * time.Millisecond) 327 | err = adb.Shell("twrp wipe dalvik") 328 | if err != nil { 329 | eEcho("Failed to wipe dalvik: " + err.Error()) 330 | exit(ErrorTWRP) 331 | } 332 | 333 | iEcho("Removing previous /data") 334 | time.Sleep(1000 * time.Millisecond) 335 | err = adb.Shell("twrp wipe data") 336 | if err != nil { 337 | eEcho("Failed to wipe data: " + err.Error()) 338 | exit(ErrorTWRP) 339 | } 340 | 341 | iEcho("Removing previous /system") 342 | time.Sleep(1000 * time.Millisecond) 343 | err = adb.Shell("twrp wipe system") 344 | if err != nil { 345 | eEcho("Failed to wipe system: " + err.Error()) 346 | exit(ErrorTWRP) 347 | } 348 | 349 | // Transfer any extra files we need to flash 350 | if currDevice.Extra_file != "" { 351 | iEcho("Transferring extra zip (firmware/etc) to your device...") 352 | if err = adb.PushFg(currDevice.Extra_file, "/sdcard"); err != nil { 353 | eEcho("Failed to push extra update zip to device: " + err.Error()) 354 | exit(ErrorAdb) 355 | } 356 | } 357 | 358 | // Transfer ROM to sdcard then install in TWRP 359 | iEcho("Transferring the NethunterOS zip to your device...") 360 | if err = adb.PushFg(currDevice.Nhos_file, "/sdcard"); err != nil { 361 | eEcho("Failed to push NethunterOS update zip to device: " + err.Error()) 362 | exit(ErrorAdb) 363 | } 364 | 365 | // Transfer filesystem with app to sdcard then install 366 | iEcho("Transferring the Nethunter filesystem zip to your device...") 367 | if err = adb.PushFg(currDevice.Nhfs_file, "/sdcard"); err != nil { 368 | eEcho("Failed to push Nethunter update zip to device: " + err.Error()) 369 | exit(ErrorAdb) 370 | } 371 | 372 | // Transfer filesystem with app to sdcard then install 373 | iEcho("Transferring the Google Apps zip to your device...") 374 | if err = adb.PushFg(currDevice.Gapps_file, "/sdcard"); err != nil { 375 | eEcho("Failed to push Google Apps zip to device: " + err.Error()) 376 | exit(ErrorAdb) 377 | } 378 | 379 | // Extras should be installed first (like Device firmware or baseband) 380 | // Otherwise NHOS will fail 381 | if currDevice.Extra_file != "" { 382 | iEcho("Installing extra zip (firmware/baseband/etc) please keep your device connected...") 383 | err = adb.Shell("twrp install /sdcard/" + currDevice.Extra_file) 384 | if err != nil { 385 | eEcho("Failed to flash extra update zip: " + err.Error()) 386 | exit(ErrorTWRP) 387 | } 388 | } 389 | 390 | // Start installer for ROM, Gapps, then Nethunter chroot & apps 391 | iEcho("Installing NethunterOS please keep your device connected...") 392 | err = adb.Shell("twrp install /sdcard/" + currDevice.Nhos_file) 393 | if err != nil { 394 | eEcho("Failed to flash Nethunter update zip: " + err.Error()) 395 | exit(ErrorTWRP) 396 | } 397 | 398 | // Install gapps? 399 | actFunc := func(opts []wmenu.Opt) error { 400 | if opts[0].ID == 0 { 401 | iEcho("Installing Gapps...") 402 | err = adb.Shell("twrp install /sdcard/" + currDevice.Gapps_file) 403 | if err != nil { 404 | eEcho("Failed to flash Google Apps: " + err.Error()) 405 | exit(ErrorTWRP) 406 | } 407 | } 408 | if opts[0].ID == 1 { 409 | fmt.Println("Skipping Gapps install") 410 | } 411 | return nil 412 | } 413 | 414 | menu := wmenu.NewMenu("Install Gapps?") // The yes or no question 415 | menu.Action(actFunc) 416 | menu.IsYesNo(0) 417 | err = menu.Run() 418 | if err != nil { 419 | log.Fatal(err) 420 | } 421 | 422 | // Pause a bit after install or TWRP gets confused 423 | // is this allways enought? 424 | time.Sleep(10000 * time.Millisecond) 425 | iEcho("Wiping your device without wiping /data/media...") 426 | err = adb.Shell("twrp wipe cache") 427 | if err != nil { 428 | eEcho("Failed to wipe cache: " + err.Error()) 429 | exit(ErrorTWRP) 430 | } 431 | time.Sleep(1000 * time.Millisecond) 432 | err = adb.Shell("twrp wipe dalvik") 433 | if err != nil { 434 | eEcho("Failed to wipe dalvik: " + err.Error()) 435 | exit(ErrorTWRP) 436 | } 437 | 438 | iEcho(MsgSuccess) 439 | err = adb.Reboot("") 440 | if err != nil { 441 | eEcho("Failed to reboot: " + err.Error()) 442 | iEcho("\nPlease reboot your device manually by going to Reboot > System > Do Not Install") 443 | exit(ErrorAdb) 444 | } 445 | // Wait for user to select install form usb option 446 | iEcho(MsgReenable) 447 | waitForOpKey("Press enter when ADB is reenabled") 448 | 449 | verifyAdbStatusOrAbort(&adb) 450 | 451 | iEcho("Rebooting your device into bootloader...") 452 | err = adb.Reboot("bootloader") 453 | if err != nil { 454 | eEcho("Failed to reboot into bootloader: " + err.Error()) 455 | exit(ErrorAdb) 456 | } 457 | 458 | time.Sleep(30000 * time.Millisecond) // 30 seconds // maybe add waitForOpKey here also? 459 | 460 | // Boot into twrp 461 | iEcho("Booting TWRP to flash Nethunter update zip.\n Swipe to allow system modification in TWRP and wait") 462 | err = fastboot.Boot(currDevice.Twrp_file) 463 | if err != nil { 464 | eEcho("Failed to boot TWRP: " + err.Error()) 465 | exit(ErrorTWRP) 466 | } 467 | 468 | // Wait for TWRP 469 | waitForOpKey("Press enter when TWRP is fully loaded & ready") 470 | 471 | time.Sleep(20000 * time.Millisecond) // maybe add waitForOpKey here also? 472 | iEcho("Installing Nethunter filesystem, please keep your device connected...") 473 | err = adb.Shell("twrp install /sdcard/" + currDevice.Nhfs_file) 474 | if err != nil { 475 | eEcho("Failed to flash Nethunter update zip: " + err.Error()) 476 | exit(ErrorTWRP) 477 | } 478 | 479 | time.Sleep(30000 * time.Millisecond) // 30 seconds // maybe add waitForOpKey here also? 480 | 481 | iEcho(MsgFinished) 482 | err = adb.Reboot("") 483 | if err != nil { 484 | eEcho("Failed to reboot: " + err.Error()) 485 | iEcho("\nPlease reboot your device manually by going to Reboot > System > Do Not Install") 486 | exit(ErrorAdb) 487 | } 488 | 489 | exit(Success) 490 | } 491 | -------------------------------------------------------------------------------- /prebuilts/linux/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nethunteros/installer/c2134647b56fe1c625137a5c89ce93726e49138a/prebuilts/linux/adb -------------------------------------------------------------------------------- /prebuilts/linux/fastboot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nethunteros/installer/c2134647b56fe1c625137a5c89ce93726e49138a/prebuilts/linux/fastboot -------------------------------------------------------------------------------- /prebuilts/linux/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # 4 | # Copyright 2015-2016 Preetam J. D'Souza 5 | # Copyright 2016 The Maru OS Project 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | cat < $2" 57 | else 58 | /bin/echo "--> $1" 59 | fi 60 | } 61 | 62 | mexit () { 63 | cd - >/dev/null 2>&1 64 | exit $1 65 | } 66 | 67 | SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd)" 68 | 69 | mecho -n "Are you ready to uninstall Maru? (yes/no): " 70 | read response 71 | 72 | if [ "$response" != "yes" ] ; then 73 | mecho "Aborting uninstall." 74 | exit 0 75 | fi 76 | 77 | cat < NUL 82 | flash-all 83 | 84 | ECHO Done! 85 | PAUSE 86 | POPD 87 | EXIT /B 0 88 | -------------------------------------------------------------------------------- /remote/download.go: -------------------------------------------------------------------------------- 1 | package remote 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/cavaliercoder/grab" 9 | ) 10 | 11 | func DownloadURL(dlLink string) { 12 | // create client 13 | client := grab.NewClient() 14 | client.UserAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36" 15 | req, _ := grab.NewRequest(".", dlLink) 16 | 17 | // Referrer needs to be set for TWRP 18 | req.HTTPRequest.Header.Set("Referer", dlLink) 19 | 20 | // start download 21 | fmt.Printf("Downloading %v...\n", req.URL()) 22 | resp := client.Do(req) 23 | fmt.Printf(" %v\n", resp.HTTPResponse.Status) 24 | 25 | // start UI loop 26 | t := time.NewTicker(500 * time.Millisecond) 27 | defer t.Stop() 28 | 29 | Loop: 30 | for { 31 | select { 32 | case <-t.C: 33 | fmt.Printf(" transferred %v / %v bytes (%.2f%%)\n", 34 | resp.BytesComplete(), 35 | resp.Size, 36 | 100*resp.Progress()) 37 | 38 | case <-resp.Done: 39 | // download is complete 40 | break Loop 41 | } 42 | } 43 | 44 | // check for errors 45 | if err := resp.Err(); err != nil { 46 | fmt.Fprintf(os.Stderr, "Download failed: %v\n", err) 47 | os.Exit(1) 48 | } 49 | 50 | fmt.Printf("Download saved to ./%v \n", resp.Filename) 51 | } 52 | -------------------------------------------------------------------------------- /strings.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package main 18 | 19 | const MsgWelcome = ` 20 | Welcome to the Nethunter installer! (forked from MaruOS installer) 21 | 22 | In order to install Nethunter you will need to: 23 | 24 | 1. Connect your device to your computer over USB 25 | 26 | 2. Enable USB Debugging on your device: 27 | 28 | 1) Go to the Settings app and scroll down to 29 | the System section 30 | 31 | NOTE: If you already have "Developer options" 32 | under System then go directly to #5 33 | 34 | 2) Tap on "About phone" 35 | 3) Tap "Build number" 7 times until you get a message 36 | that says you are now a developer 37 | 4) Go back to the main Settings app 38 | 5) Tap on "Developer options" 39 | 6) Ensure that "USB debugging" is enabled 40 | 7) You may need to restart adb if you don't get an RSA key box (adb kill-server) 41 | 8) Tap "OK" if you see a dialog asking you to allow 42 | USB Debugging for your computer's RSA key fingerprint 43 | 44 | WARNING: Installing Nethunter will wipe all your personal data 45 | so make sure you first back-up anything important! 46 | ` 47 | 48 | const MsgIncompleteZip = ` 49 | Hmm, looks like your installer is a missing a few things. 50 | 51 | Are you running this install script outside the directory you unzipped the installer in? 52 | ` 53 | 54 | const msgFixAdb = ` 55 | Please ensure that: 56 | 57 | 1. Your device is connected to your computer over USB 58 | 2. You have USB Debugging enabled (see above for instructions) 59 | 3. You unlock your device and tap "OK" if you see a dialog asking you 60 | to allow USB Debugging for your computer's RSA key fingerprint 61 | 62 | If you are on Windows, please ensure you have the Google USB Driver properly 63 | installed for your device as described in HELP.txt (this is the main source of 64 | problems on Windows!) 65 | 66 | Go ahead and re-run the installer when you're ready. 67 | ` 68 | const MsgAdbIssue = "\nHmm, there was an issue communicating with your device.\n" + msgFixAdb 69 | 70 | const MsgFastbootNoDeviceFound = ` 71 | Hmm, your device can't be found. Please ensure that your device is connected to 72 | your computer over USB. 73 | ` 74 | 75 | const MsgFixPerms = ` 76 | It looks like you are missing some USB permissions. 77 | 78 | Please follow the instructions below depending on your platform: 79 | 80 | Linux 81 | ----- 82 | 83 | On certain Linux distributions (Ubuntu 14.04 for example), you will need to 84 | explicitly add permissions to access USB devices: 85 | 86 | 1. Disconnect your device from USB 87 | 88 | 2. Run this in a terminal (requires sudo): 89 | 90 | $ wget -S -O - https://source.android.com/source/51-android.txt | sed "s//$USER/" | sudo tee >/dev/null /etc/udev/rules.d/51-android.rules; sudo udevadm control --reload-rules 91 | 92 | 3. Re-connect your device over USB and re-run this installer 93 | 94 | Windows 95 | ------- 96 | 97 | Please ensure you have the Google USB Driver properly installed for your device 98 | as described in HELP.txt (this is the main source of problems on Windows!) 99 | 100 | ` 101 | 102 | const MsgUnlockSuccess = ` 103 | Successfully unlocked bootloader! 104 | 105 | Your device will need to reboot before continuing. It will factory reset, so 106 | this reboot can take a few minutes longer than usual. 107 | 108 | To continue the installation process, please re-run this script after your 109 | device completely boots up and you have re-enabled USB Debugging. 110 | ` 111 | 112 | const MsgSuccess = ` 113 | Installation of base OS complete! 114 | 115 | The first boot will take 2-3 mins as Nethunter sets up your device so please be 116 | patient. 117 | 118 | Rebooting into NethunterOS...were almost there! We still need to install Kali filesystem! 119 | ` 120 | 121 | const MsgReenable = ` 122 | Please reenable ADB one more time to flash the filesystem to device. 123 | 124 | 1. Connect your device to your computer over USB 125 | 126 | 2. Enable USB Debugging on your device: 127 | 128 | 1) Go to the Settings app and scroll down to 129 | the System section 130 | 131 | NOTE: If you already have "Developer options" 132 | under System then go directly to #5 133 | 134 | 2) Tap on "About phone" 135 | 3) Tap "Build number" 7 times until you get a message 136 | that says you are now a developer 137 | 4) Go back to the main Settings app 138 | 5) Tap on "Developer options" 139 | 6) Ensure that "USB debugging" is enabled 140 | 7) You may need to restart adb if you don't get an RSA key box (adb kill-server) 141 | 8) Tap "OK" if you see a dialog asking you to allow 142 | USB Debugging for your computer's RSA key fingerprint 143 | 144 | ` 145 | const MsgFinished = ` 146 | All done! 147 | 148 | Run the Nethunter app first to discovery chroot. Make sure to grant super su 149 | permissions. 150 | 151 | Terminal 152 | ` -------------------------------------------------------------------------------- /tests/functional.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Copyright 2017 The Maru OS Project 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | set -u 20 | 21 | readonly SCRIPT_DIR="$(dirname "$0")" 22 | source "${SCRIPT_DIR}/test.sh" 23 | 24 | # keep in sync with exit codes in main.go 25 | readonly SUCCESS=0 26 | readonly SUCCESS_BASE=$(( 1 << 5 )) 27 | readonly SUCCESS_USER_ABORT=$(( SUCCESS_BASE + 1 )) 28 | readonly SUCCESS_BOOTLOADER_UNLOCKED=$(( SUCCESS_BASE + 2 )) 29 | readonly ERROR_BASE=$(( 1 << 6 )) 30 | readonly ERROR_PREREQS=$(( ERROR_BASE + 1 )) 31 | readonly ERROR_USER_INPUT=$(( ERROR_BASE + 2 )) 32 | readonly ERROR_USB_PERMS=$(( ERROR_BASE + 3 )) 33 | readonly ERROR_ADB=$(( ERROR_BASE + 4 )) 34 | readonly ERROR_FASTBOOT=$(( ERROR_BASE + 5 )) 35 | readonly ERROR_REMOTE=$(( ERROR_BASE + 6 )) 36 | readonly ERROR_TWRP=$(( ERROR_BASE + 7 )) 37 | 38 | mock_fastboot () { 39 | local readonly in_bootloader="$1" 40 | local readonly product="$2" 41 | local readonly lock_state="$3" 42 | 43 | local unlocked="false" 44 | if [ "$lock_state" = "unlocked" ] ; then 45 | unlocked="true" 46 | fi 47 | 48 | cat >fastboot <adb </dev/null 178 | } 179 | 180 | trap teardown EXIT 181 | 182 | echo "Installer should..." 183 | echo 184 | 185 | techo "abort if missing a complete zip" 186 | echo "yes" | ./install >/dev/null 187 | tassert_eq $ERROR_PREREQS $? 188 | 189 | setup 190 | 191 | techo "abort if user answers 'no' to prompt" 192 | mock_fastboot "true" "flo" "locked" 193 | echo "no" | ./install >/dev/null 194 | tassert_eq $SUCCESS_USER_ABORT $? 195 | 196 | techo "unlock a locked flo" 197 | mock_fastboot "true" "flo" "locked" 198 | echo "yes" | ./install >/dev/null 199 | tassert_eq $SUCCESS_BOOTLOADER_UNLOCKED $? 200 | 201 | techo "unlock a generic locked device" 202 | mock_fastboot "true" "hammerhead" "locked" 203 | echo "yes" | ./install >/dev/null 204 | tassert_eq $SUCCESS_BOOTLOADER_UNLOCKED $? 205 | 206 | techo "abort if using an unsupported device" 207 | mock_fastboot "true" "somefakedevice" "unlocked" 208 | echo "yes" | ./install >/dev/null 209 | tassert_eq $ERROR_REMOTE $? 210 | 211 | techo "abort if using an unsupported device with similar name" 212 | mock_fastboot "true" "hammer" "unlocked" 213 | echo "yes" | ./install >/dev/null 214 | tassert_eq $ERROR_REMOTE $? 215 | 216 | techo "install succesfully on unlocked flo with workaround" 217 | mock_fastboot "true" "flo" "unlocked" 218 | echo "yes" | ./install >/dev/null 219 | tassert_eq $SUCCESS $? 220 | 221 | techo "install succesfully on a supported unlocked device" 222 | mock_fastboot "true" "hammerhead" "unlocked" 223 | echo "yes" | ./install >/dev/null 224 | tassert_eq $SUCCESS $? 225 | 226 | # misc tests 227 | 228 | techo "use a valid URL for wgetting 51-android.rules" 229 | grep wget /dev/null 230 | tassert_eq $SUCCESS $? 231 | 232 | texit 233 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017 Preetam J. D'Souza 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | _NUM_TESTS=0 18 | _NUM_PASS=0 19 | 20 | _test_pass() { 21 | echo "[ PASS ]" 22 | } 23 | 24 | _test_fail () { 25 | echo "[ FAIL ]" 26 | } 27 | 28 | techo () { 29 | printf " %-60s" "$@" 30 | } 31 | 32 | treport () { 33 | echo 34 | echo "$_NUM_PASS of $_NUM_TESTS tests passed." 35 | } 36 | 37 | texit () { 38 | treport 39 | exit $(( _NUM_TESTS - _NUM_PASS )) 40 | } 41 | 42 | tassert_eq () { 43 | local readonly expected="$1" 44 | local readonly value="$2" 45 | 46 | (( ++_NUM_TESTS )) 47 | if [ "$expected" = "$value" ] ; then 48 | (( ++_NUM_PASS )) 49 | _test_pass 50 | else 51 | _test_fail 52 | fi 53 | } 54 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Maru OS Project 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package main 18 | 19 | // Filled in at build time. 20 | var Version = "undefined" 21 | --------------------------------------------------------------------------------