Skip to content

Commit

Permalink
Improve error dialog, add clear data button, make unziup and 7zip wor…
Browse files Browse the repository at this point in the history
…k on armhf, add fallback to unzip
  • Loading branch information
alufers committed Mar 25, 2023
1 parent 1fbfba0 commit 0de1453
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 106 deletions.
200 changes: 104 additions & 96 deletions app/app/src/main/java/com/octo4a/repository/BootstrapRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,126 +60,134 @@ class BootstrapRepositoryImpl(
}

override suspend fun setupBootstrap() {
withContext(Dispatchers.IO) {
val PREFIX_FILE = File(PREFIX_PATH)
if (PREFIX_FILE.isDirectory) {
return@withContext
}
withContext(Dispatchers.IO) {
val PREFIX_FILE = File(PREFIX_PATH)
if (PREFIX_FILE.isDirectory) {
return@withContext
}

try {
val bootstrapReleases =
githubRepository.getNewestReleases("feelfreelinux/android-linux-bootstrap")
val arch = getArchString()
try {
val bootstrapReleases =
githubRepository.getNewestReleases("feelfreelinux/android-linux-bootstrap")
val arch = getArchString()

val release = bootstrapReleases.firstOrNull {
it.assets.any { asset -> asset.name.contains(arch) }
}
val release = bootstrapReleases.firstOrNull {
it.assets.any { asset -> asset.name.contains(arch) }
}

val asset = release?.assets?.first { asset -> asset.name.contains(arch) }
logger.log(this) { "Arch: $arch" }
val asset = release?.assets?.first { asset -> asset.name.contains(arch) }
logger.log(this) { "Arch: $arch" }


val STAGING_PREFIX_PATH = "${FILES_PATH}/bootstrap-staging"
val STAGING_PREFIX_FILE = File(STAGING_PREFIX_PATH)
val STAGING_PREFIX_PATH = "${FILES_PATH}/bootstrap-staging"
val STAGING_PREFIX_FILE = File(STAGING_PREFIX_PATH)

if (STAGING_PREFIX_FILE.exists()) {
deleteFolder(STAGING_PREFIX_FILE)
}
if (STAGING_PREFIX_FILE.exists()) {
deleteFolder(STAGING_PREFIX_FILE)
}

val buffer = ByteArray(8096)
val symlinks = ArrayList<Pair<String, String>>(50)
val buffer = ByteArray(8096)
val symlinks = ArrayList<Pair<String, String>>(50)

val urlPrefix = asset!!.browserDownloadUrl
logger.log(this) { "Downloading bootstrap ${release?.tagName} from ${urlPrefix}" }
val urlPrefix = asset!!.browserDownloadUrl
logger.log(this) { "Downloading bootstrap ${release?.tagName} from ${urlPrefix}" }

val sslcontext = SSLContext.getInstance("TLSv1")
sslcontext.init(null, null, null)
val noSSLv3Factory: SSLSocketFactory = TLSSocketFactory()
val sslcontext = SSLContext.getInstance("TLSv1")
sslcontext.init(null, null, null)
val noSSLv3Factory: SSLSocketFactory = TLSSocketFactory()

HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory)
val connection: HttpsURLConnection =
URL(urlPrefix).openConnection() as HttpsURLConnection
connection.sslSocketFactory = noSSLv3Factory
connection.connect()
val code = connection.responseCode
logger.log(this) { "Request to ${connection.url} returned status code $code" }
if (code > 399) {
HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory)
val connection: HttpsURLConnection =
URL(urlPrefix).openConnection() as HttpsURLConnection
connection.sslSocketFactory = noSSLv3Factory
connection.connect()
val code = connection.responseCode
logger.log(this) { "Request to ${connection.url} returned status code $code" }
if (code > 399) {

throw RuntimeException(
"Fetching ${connection.url} failed with status code $code"
)
}
ZipInputStream(connection.inputStream).use { zipInput ->
var zipEntry = zipInput.nextEntry
while (zipEntry != null) {

val zipEntryName = zipEntry.name
val targetFile = File(STAGING_PREFIX_PATH, zipEntryName)
val isDirectory = zipEntry.isDirectory

ensureDirectoryExists(if (isDirectory) targetFile else targetFile.parentFile)

if (!isDirectory) {
FileOutputStream(targetFile).use { outStream ->
var readBytes = zipInput.read(buffer)
while ((readBytes) != -1) {
outStream.write(buffer, 0, readBytes)
readBytes = zipInput.read(buffer)
}
throw RuntimeException(
"Fetching ${connection.url} failed with status code $code"
)
}
ZipInputStream(connection.inputStream).use { zipInput ->
var zipEntry = zipInput.nextEntry
while (zipEntry != null) {

val zipEntryName = zipEntry.name
val targetFile = File(STAGING_PREFIX_PATH, zipEntryName)
val isDirectory = zipEntry.isDirectory

ensureDirectoryExists(if (isDirectory) targetFile else targetFile.parentFile)

if (!isDirectory) {
FileOutputStream(targetFile).use { outStream ->
var readBytes = zipInput.read(buffer)
while ((readBytes) != -1) {
outStream.write(buffer, 0, readBytes)
readBytes = zipInput.read(buffer)
}
}
zipEntry = zipInput.nextEntry
}
zipEntry = zipInput.nextEntry
}
}

if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) {
throw RuntimeException("Unable to rename staging folder")
}
logger.log(this) { "Bootstrap extracted, setting it up..." }
runCommand("ls", prooted = false).waitAndPrintOutput(logger)
runCommand("chmod -R 700 .", prooted = false).waitAndPrintOutput(logger)
if (shouldUsePre5Bootstrap()) {
runCommand(
"rm -r root && mv root-pre5 root",
prooted = false
).waitAndPrintOutput(logger)
}

if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) {
throw RuntimeException("Unable to rename staging folder")
}
logger.log(this) { "Bootstrap extracted, setting it up..." }
runCommand("ls", prooted = false).waitAndPrintOutput(logger)
runCommand("chmod -R 700 .", prooted = false).waitAndPrintOutput(logger)
if (shouldUsePre5Bootstrap()) {
runCommand(
"sh install-bootstrap.sh",
"rm -r root && mv root-pre5 root",
prooted = false
).waitAndPrintOutput(logger)
runCommand("sh add-user.sh octoprint", prooted = false).waitAndPrintOutput(
logger
)
runCommand("cat /etc/motd").waitAndPrintOutput(logger)
runCommand("env").waitAndPrintOutput(logger)
runCommand("ls /").waitAndPrintOutput(logger)
}

// Setup ssh
runCommand(
"sh install-bootstrap.sh",
prooted = false
).waitAndPrintOutput(logger)
runCommand("sh add-user.sh octoprint", prooted = false).waitAndPrintOutput(
logger
)
runCommand("cat /etc/motd").waitAndPrintOutput(logger)
runCommand("env").waitAndPrintOutput(logger)
runCommand("ls /").waitAndPrintOutput(logger)

// Setup ssh
runCommand(
"apk add openssh-server curl bash unzip",
bash = false
).waitAndPrintOutput(logger)
runCommand("echo \"PermitRootLogin yes\" >> /etc/ssh/sshd_config").waitAndPrintOutput(
logger
)
runCommand("ssh-keygen -A").waitAndPrintOutput(logger)

logger.log(this) { "Installing p7zip..." }

try {
runCommand(
"apk add openssh-server curl bash unzip",
"apk add p7zip",
bash = false
).waitAndPrintOutput(logger)
} catch (e: java.lang.Exception) {
logger.log { "Failed to install p7zip from release repository, trying Alpine edge..." }
logger.log { "This may be caused by the fact that p7zip is missing on armhf Alpine 3.17, see: https://gitlab.alpinelinux.org/alpine/aports/-/commits/master/main/p7zip/APKBUILD" }
runCommand(
"apk add p7zip --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main",
bash = false
).waitAndPrintOutput(logger)
runCommand("echo \"PermitRootLogin yes\" >> /etc/ssh/sshd_config").waitAndPrintOutput(
logger
)
runCommand("ssh-keygen -A").waitAndPrintOutput(logger)

logger.log(this) { "Setting p7zip" }

if (arch == "armhf" || arch == "arm" || arch == "armv7" || arch == "") {
logger.log(this) { "On armhf " }
}

logger.log(this) { "Bootstrap installation done" }

return@withContext
} catch (e: Exception) {
throw (e)
} finally {
}

return@withContext
} catch (e: Exception) {
throw (e)
} finally {
}
}

}

Expand Down Expand Up @@ -213,7 +221,7 @@ class BootstrapRepositoryImpl(
root: Boolean,
bash: Boolean
): Process {
logger.run { ">$command" }
logger.log { ">$command" }
val FILES = "/data/data/com.octo4a/files"
val directory = File(filesPath)
if (!directory.exists()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
package com.octo4a.repository

import android.R.attr.path
import android.R.attr.start
import android.content.Context
import android.os.Environment
import android.util.Log
import com.google.gson.JsonParser
import com.octo4a.serial.VSPPty
import com.octo4a.serial.VirtualSerialDriver
import com.octo4a.utils.*
import com.octo4a.utils.preferences.MainPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import org.json.JSONObject
import org.yaml.snakeyaml.Yaml
import java.io.File
import java.io.FileWriter
import java.lang.Exception
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.reflect.Field
import java.util.regex.Matcher
import java.util.regex.Pattern
Expand Down Expand Up @@ -155,7 +150,14 @@ class OctoPrintHandlerRepositoryImpl(
logger
)
runCommand("ls -lah").waitAndPrintOutput(logger)
runCommand("7z x -y octoprint.zip").waitAndPrintOutput(logger)
try {
runCommand("7z x -y octoprint.zip").waitAndPrintOutput(logger)
} catch(e: java.lang.Exception) {
logger.log { "7zip extraction failed: $e" }
logger.log { "Trying to use unzip" }
runCommand("unzip octoprint.zip").waitAndPrintOutput(logger)
}

}
_serverState.emit(ServerStatus.InstallingDependencies)
bootstrapRepository.apply {
Expand All @@ -181,6 +183,11 @@ class OctoPrintHandlerRepositoryImpl(
} catch (e: java.lang.Exception) {
_serverState.emit(ServerStatus.InstallationError)
_installErrorDescription.emit(e.toString())
val sw = StringWriter()
val pw = PrintWriter(sw)
e.printStackTrace(pw)

logger.log{ "An exception has occurred at:\n$sw\nException:\n${e.toString()}"}
}
}

Expand Down
21 changes: 21 additions & 0 deletions app/app/src/main/java/com/octo4a/ui/InstallationActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.octo4a.ui

import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
Expand Down Expand Up @@ -63,6 +66,24 @@ class InstallationActivity : AppCompatActivity() {
val logsFragment = TerminalSheetDialog()
logsFragment.show(supportFragmentManager, logsFragment.tag)
}
clearDataAndRestart.setOnClickListener{
try {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
// clearing app data
if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) {
activityManager.clearApplicationUserData()
} else {
val packageName = applicationContext.packageName
val runtime = Runtime.getRuntime();
runtime.exec("pm clear $packageName");
}


Runtime.getRuntime().exit(0)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

private fun setItemsState(status: ServerStatus) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,5 @@ class ServerFragment : Fragment() {
val dialog = builder.create()
dialog.show()


}
}
13 changes: 13 additions & 0 deletions app/app/src/main/java/com/octo4a/utils/ProcessUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ fun Process.waitAndPrintOutput(logger: LoggerRepository, type: LogType = LogType
logger.log(this, type) { it }
outputStr += it
}


val exitCode = waitFor()
if (exitCode != 0) {
val logLines = outputStr
// replace useless proot warning
.replace("proot warning: can't sanitize binding \"/data/data/com.octo4a/files/serialpipe\": No such file or directory", "")
.lines()
.takeLast(2)
.joinToString("\n") { it.take(50) }
throw RuntimeException("Process exited with error code ${exitCode}. $logLines")
}

return outputStr
}

Expand Down
15 changes: 14 additions & 1 deletion app/app/src/main/res/layout/activity_installation_progress.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="visible"
>

<TextView
Expand Down Expand Up @@ -141,7 +142,7 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/seeLogsButton"
android:layout_width="match_parent"
android:background="@color/design_default_color_on_secondary"
android:background="@color/cardview_light_background"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:text="@string/see_logs"
Expand All @@ -150,6 +151,18 @@
app:cornerRadius="16dp"


/>

<com.google.android.material.button.MaterialButton
android:id="@+id/clearDataAndRestart"
android:layout_width="match_parent"
android:background="@android:color/transparent"
android:textColor="#000"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:text="@string/clear_data_and_restart"
android:textStyle="bold"
app:cornerRadius="16dp"
/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
Expand Down
Loading

0 comments on commit 0de1453

Please sign in to comment.