From dc7c03661d157e7cbdbe9dd86aa8031d4b50960d Mon Sep 17 00:00:00 2001
From: Mbucari <37587114+Mbucari@users.noreply.github.com>
Date: Mon, 13 Feb 2023 22:25:44 -0700
Subject: [PATCH] Add auto update to linux and macos
---
.github/workflows/build-linux.yml | 2 +-
.releaseindex.json | 4 +-
Documentation/InstallOnMac.md | 18 +-
Scripts/targz2linuxbundle.sh | 5 +-
Scripts/targz2macosbundle.sh | 4 +-
.../LibationAvalonia/LibationAvalonia.csproj | 20 --
Source/LibationAvalonia/Program.cs | 9 +-
.../Views/MainWindow.Settings.cs | 2 -
.../Views/MainWindow.Update.cs | 54 +----
.../LibationFileManager/IInteropFunctions.cs | 5 +-
.../NullInteropFunctions.cs | 10 +-
.../LibationWinForms/GridView/ProductsGrid.cs | 2 +-
.../LinuxConfigApp}/Libation.desktop | 0
.../LinuxConfigApp/LinuxConfigApp.csproj | 24 +++
.../LoadByOS/LinuxConfigApp/LinuxInterop.cs | 65 +++++-
.../Properties/Resources.Designer.cs | 73 +++++++
.../LinuxConfigApp/Properties/Resources.resx | 124 ++++++++++++
.../LinuxConfigApp/Resources/runasroot.sh | 188 ++++++++++++++++++
.../LinuxConfigApp}/glass-with-glow_256.svg | 0
.../MacOSConfigApp}/Info.plist | 4 +
.../MacOSConfigApp/MacOSConfigApp.csproj | 9 +
.../LoadByOS/MacOSConfigApp/MacOSInterop.cs | 74 ++++++-
.../MacOSConfigApp}/libation.icns | Bin
.../LoadByOS/WindowsConfigApp/WinInterop.cs | 28 ++-
.../WindowsConfigApp/WindowsConfigApp.csproj | 6 +
.../WindowsConfigApp}/ZipExtractor.exe | Bin
26 files changed, 631 insertions(+), 99 deletions(-)
rename Source/{LibationAvalonia => LoadByOS/LinuxConfigApp}/Libation.desktop (100%)
create mode 100644 Source/LoadByOS/LinuxConfigApp/Properties/Resources.Designer.cs
create mode 100644 Source/LoadByOS/LinuxConfigApp/Properties/Resources.resx
create mode 100644 Source/LoadByOS/LinuxConfigApp/Resources/runasroot.sh
rename Source/{LibationAvalonia => LoadByOS/LinuxConfigApp}/glass-with-glow_256.svg (100%)
rename Source/{LibationAvalonia => LoadByOS/MacOSConfigApp}/Info.plist (87%)
rename Source/{LibationAvalonia => LoadByOS/MacOSConfigApp}/libation.icns (100%)
rename Source/{LibationAvalonia => LoadByOS/WindowsConfigApp}/ZipExtractor.exe (100%)
diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index 93f235f8..ec8b315d 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -66,7 +66,7 @@ jobs:
id: zip
working-directory: ./Source/bin/Publish/${{ matrix.os }}-${{ matrix.release_name }}
run: |
- delfiles=("libmp3lame.x86.dll" "libmp3lame.x64.dll" "ffmpegaac.x86.dll" "ffmpegaac.x64.dll" "ZipExtractor.exe")
+ delfiles=("libmp3lame.x86.dll" "libmp3lame.x64.dll" "ffmpegaac.x86.dll" "ffmpegaac.x64.dll")
for n in "${delfiles[@]}"; do rm "$n"; done
osbuild="$(echo '${{ matrix.os }}' | tr '[:upper:]' '[:lower:]')"
artifact="Libation.${{ steps.get_version.outputs.version }}-${osbuild}-${{ matrix.release_name }}"
diff --git a/.releaseindex.json b/.releaseindex.json
index 6acac9e8..65af875b 100644
--- a/.releaseindex.json
+++ b/.releaseindex.json
@@ -1,6 +1,6 @@
{
"WindowsClassic": "Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-classic\\.zip",
"WindowsAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-chardonnay\\.zip",
- "LinuxAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay",
- "MacOSAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-macos-chardonnay"
+ "LinuxAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay\\.deb",
+ "MacOSAvalonia": "Libation\\.app-x64-\\d+\\.\\d+\\.\\d+\\.tgz"
}
diff --git a/Documentation/InstallOnMac.md b/Documentation/InstallOnMac.md
index 7c40b106..f565999e 100644
--- a/Documentation/InstallOnMac.md
+++ b/Documentation/InstallOnMac.md
@@ -9,18 +9,14 @@ This walkthrough should get you up and running with Libation on your Mac.
## Install Libation
-- Download the `Libation.app.x.x.x.tar.gz` file from the latest release and extract it.
+- Download the `Libation.app-x64-x.x.x.tgz` file from the latest release and extract it.
- Move the extracted Libation app bundle to your applications folder.
- Open a terminal (Go > Utilities > Terminal)
-- In the terminal type the following commands
- - `sudo spctl --add --label "Libation" /Applications/Libation.app` (you'll be prompted to enter your password.)
- - `sudo spctl --master-disable`
-- Keep the terminal open and run the Libation app
-- Go back to terminal and type the following command
- - `sudo spctl --master-enable`
-- Close the terminal
-
-Libation is now registered with gatekeeper and will run even when gatekeeper is turned back on.
+- Copy/paste/run the following command (you'll be prompted to enter your password)
+ ```Console
+ sudo spctl --master-disable && sudo spctl --add --label "Libation" /Applications/Libation.app && open /Applications/Libation.app && sudo spctl --master-enable
+ ```
+- Close the terminal and use Libation!
## Running Hangover
@@ -37,6 +33,8 @@ open /Applications/Libation.app --args cli
```
To use LibationCli from an unsandboxed terminal, you must disable gatekeeper again and run the program directly at `/Applications/Libation.app/Contents/MacOS/LibationCli`
+Then use `./LibationCli` to execute a command.
+
## Get Libation running on Mac
[Run Libation on MacOS](https://user-images.githubusercontent.com/37587114/213933357-983d8ede-2738-4b32-9c6e-40de21ff09c2.mp4)
diff --git a/Scripts/targz2linuxbundle.sh b/Scripts/targz2linuxbundle.sh
index 145e2a3a..b4806ad8 100644
--- a/Scripts/targz2linuxbundle.sh
+++ b/Scripts/targz2linuxbundle.sh
@@ -106,7 +106,10 @@ ln -s /usr/lib/libation/Hangover /usr/bin/hangover
ln -s /usr/lib/libation/LibationCli /usr/bin/libationcli
# Increase the maximum number of inotify instances
-echo fs.inotify.max_user_instances=524288 | tee -a /etc/sysctl.conf && sysctl -p
+
+if ! grep -q 'fs.inotify.max_user_instances=524288' /etc/sysctl.conf; then
+ echo fs.inotify.max_user_instances=524288 | tee -a /etc/sysctl.conf && sysctl -p
+fi
# workaround until this file is moved to the user's home directory
touch /usr/lib/libation/appsettings.json
diff --git a/Scripts/targz2macosbundle.sh b/Scripts/targz2macosbundle.sh
index 1defb754..dc121216 100644
--- a/Scripts/targz2macosbundle.sh
+++ b/Scripts/targz2macosbundle.sh
@@ -69,7 +69,7 @@ echo "Set Libation version number..."
sed -i -e "s/VERSION_STRING/$VERSION/" "$BUNDLE_CONTENTS/Info.plist"
echo "deleting unneeded files.."
-delfiles=("libmp3lame.x64.so" "ffmpegaac.x64.so" "Libation.desktop" "libation.icns" "Info.plist" "glass-with-glow_256.svg")
+delfiles=("libmp3lame.x64.so" "ffmpegaac.x64.so" "libation.icns" "Info.plist")
for n in "${delfiles[@]}"; do rm "$BUNDLE_MACOS/$n"; done
echo "Creating app bundle: $BUNDLE-$VERSION.tar.gz"
@@ -77,7 +77,7 @@ tar -czvf "$BUNDLE-$VERSION.tar.gz" "$BUNDLE"
mkdir bundle
echo "moving to ./bundle/$BUNDLE-$VERSION.tar.gz"
-mv "$BUNDLE-$VERSION.tar.gz" "./bundle/$BUNDLE-$VERSION.tar.gz"
+mv "$BUNDLE-$VERSION.tar.gz" "./bundle/$BUNDLE-x64-$VERSION.tgz"
rm -r "$BUNDLE"
diff --git a/Source/LibationAvalonia/LibationAvalonia.csproj b/Source/LibationAvalonia/LibationAvalonia.csproj
index 4ac37e7a..40399412 100644
--- a/Source/LibationAvalonia/LibationAvalonia.csproj
+++ b/Source/LibationAvalonia/LibationAvalonia.csproj
@@ -116,26 +116,6 @@
-
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
-
-
diff --git a/Source/LibationAvalonia/Program.cs b/Source/LibationAvalonia/Program.cs
index 79efb369..4c5f861b 100644
--- a/Source/LibationAvalonia/Program.cs
+++ b/Source/LibationAvalonia/Program.cs
@@ -16,15 +16,20 @@ namespace LibationAvalonia
{
static void Main(string[] args)
{
- if (Configuration.IsMacOs && args != null && args.Length != 0 && args[0] == "hangover")
+
+ if (Configuration.IsMacOs && args?.Length > 0 && args[0] == "hangover")
{
//Launch the Hangover app within the sandbox
+ //We can do this because we're already executing inside the sandbox.
+ //Any process created in the sandbox executes in the same sandbox.
+ //Unfortunately, all sandbox files are read/execute, so no writing!
+
Assembly asm = Assembly.GetExecutingAssembly();
string path = Path.GetDirectoryName(asm.Location);
Process.Start("Hangover" + (Configuration.IsWindows ? ".exe" : ""));
return;
}
- if (Configuration.IsMacOs && args != null && args.Length != 0 && args[0] == "cli")
+ if (Configuration.IsMacOs && args?.Length > 0 && args[0] == "cli")
{
//Open a new Terminal in the sandbox
Assembly asm2 = Assembly.GetExecutingAssembly();
diff --git a/Source/LibationAvalonia/Views/MainWindow.Settings.cs b/Source/LibationAvalonia/Views/MainWindow.Settings.cs
index add4ecfa..9cb02447 100644
--- a/Source/LibationAvalonia/Views/MainWindow.Settings.cs
+++ b/Source/LibationAvalonia/Views/MainWindow.Settings.cs
@@ -1,7 +1,5 @@
using LibationFileManager;
using System;
-using System.Linq;
-using System.Reflection;
namespace LibationAvalonia.Views
{
diff --git a/Source/LibationAvalonia/Views/MainWindow.Update.cs b/Source/LibationAvalonia/Views/MainWindow.Update.cs
index 99d2bfa8..a6858d94 100644
--- a/Source/LibationAvalonia/Views/MainWindow.Update.cs
+++ b/Source/LibationAvalonia/Views/MainWindow.Update.cs
@@ -14,7 +14,7 @@ namespace LibationAvalonia.Views
Opened += async (_, _) => await checkForUpdates();
}
- private async Task checkForUpdates()
+ private async Task checkForUpdates()
{
async Task downloadUpdate(UpgradeProperties upgradeProperties)
{
@@ -26,7 +26,7 @@ namespace LibationAvalonia.Views
//Silently download the update in the background, save it to a temp file.
- var zipFile = Path.GetTempFileName();
+ var zipFile = Path.Combine(Path.GetTempPath(), Path.GetFileName(upgradeProperties.ZipUrl));
try
{
System.Net.Http.HttpClient cli = new();
@@ -42,36 +42,6 @@ namespace LibationAvalonia.Views
return zipFile;
}
- void runWindowsUpgrader(string zipFile)
- {
- var thisExe = Environment.ProcessPath;
- var thisDir = Path.GetDirectoryName(thisExe);
-
- var zipExtractor = Path.Combine(Path.GetTempPath(), "ZipExtractor.exe");
-
- File.Copy("ZipExtractor.exe", zipExtractor, overwrite: true);
-
- var psi = new System.Diagnostics.ProcessStartInfo()
- {
- FileName = zipExtractor,
- UseShellExecute = true,
- Verb = "runas",
- WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal,
- CreateNoWindow = true,
- ArgumentList =
- {
- "--input",
- zipFile,
- "--output",
- thisDir,
- "--executable",
- thisExe
- }
- };
-
- System.Diagnostics.Process.Start(psi);
- }
-
try
{
var upgradeProperties = await Task.Run(LibationScaffolding.GetLatestRelease);
@@ -83,26 +53,22 @@ namespace LibationAvalonia.Views
if (config.GetString(propertyName: ignoreUpdate) == upgradeProperties.LatestRelease.ToString())
return;
- var notificationResult = await new UpgradeNotificationDialog(upgradeProperties, Configuration.IsWindows).ShowDialog(this);
+ var interop = InteropFactory.Create();
+
+ var notificationResult = await new UpgradeNotificationDialog(upgradeProperties, interop.CanUpdate).ShowDialog(this);
if (notificationResult == DialogResult.Ignore)
config.SetString(upgradeProperties.LatestRelease.ToString(), ignoreUpdate);
- if (notificationResult != DialogResult.OK || !Configuration.IsWindows) return;
+ if (notificationResult != DialogResult.OK) return;
//Download the update file in the background,
- //then wire up installaion on window close.
+ string updateBundle = await downloadUpdate(upgradeProperties);
- string zipFile = await downloadUpdate(upgradeProperties);
+ if (string.IsNullOrEmpty(updateBundle) || !File.Exists(updateBundle)) return;
- if (string.IsNullOrEmpty(zipFile) || !File.Exists(zipFile))
- return;
-
- Closed += (_, _) =>
- {
- if (File.Exists(zipFile))
- runWindowsUpgrader(zipFile);
- };
+ //Install the update
+ interop.InstallUpdate(updateBundle);
}
catch (Exception ex)
{
diff --git a/Source/LibationFileManager/IInteropFunctions.cs b/Source/LibationFileManager/IInteropFunctions.cs
index 423d438d..fd1cca37 100644
--- a/Source/LibationFileManager/IInteropFunctions.cs
+++ b/Source/LibationFileManager/IInteropFunctions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
namespace LibationFileManager
{
@@ -6,6 +7,8 @@ namespace LibationFileManager
{
void SetFolderIcon(string image, string directory);
void DeleteFolderIcon(string directory);
- void CopyTextToClipboard(string text);
+ Process RunAsRoot(string exe, string args);
+ void InstallUpdate(string updateBundle);
+ bool CanUpdate { get; }
}
}
diff --git a/Source/LibationFileManager/NullInteropFunctions.cs b/Source/LibationFileManager/NullInteropFunctions.cs
index 7f0e6908..bf995d11 100644
--- a/Source/LibationFileManager/NullInteropFunctions.cs
+++ b/Source/LibationFileManager/NullInteropFunctions.cs
@@ -1,14 +1,18 @@
using System;
+using System.Diagnostics;
namespace LibationFileManager
{
public class NullInteropFunctions : IInteropFunctions
{
- public NullInteropFunctions() { }
+
+ public NullInteropFunctions() { }
public NullInteropFunctions(params object[] values) { }
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
- public void CopyTextToClipboard(string text) => throw new PlatformNotSupportedException();
- }
+ public bool CanUpdate => throw new PlatformNotSupportedException();
+ public Process RunAsRoot(string exe, string args) => throw new PlatformNotSupportedException();
+ public void InstallUpdate(string updateBundle) => throw new PlatformNotSupportedException();
+ }
}
diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs
index e19fb4bc..7766a73c 100644
--- a/Source/LibationWinForms/GridView/ProductsGrid.cs
+++ b/Source/LibationWinForms/GridView/ProductsGrid.cs
@@ -123,7 +123,7 @@ namespace LibationWinForms.GridView
{
var dgv = (DataGridView)sender;
var text = dgv[e.ColumnIndex, e.RowIndex].FormattedValue.ToString();
- InteropFactory.Create().CopyTextToClipboard(text);
+ Clipboard.SetDataObject(text, false, 5, 150);
}
catch { }
});
diff --git a/Source/LibationAvalonia/Libation.desktop b/Source/LoadByOS/LinuxConfigApp/Libation.desktop
similarity index 100%
rename from Source/LibationAvalonia/Libation.desktop
rename to Source/LoadByOS/LinuxConfigApp/Libation.desktop
diff --git a/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj b/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj
index 9a7e5ba2..9d9a1b3e 100644
--- a/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj
+++ b/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj
@@ -34,4 +34,28 @@
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
\ No newline at end of file
diff --git a/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs b/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs
index 08324141..bf488806 100644
--- a/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs
+++ b/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs
@@ -1,14 +1,73 @@
using LibationFileManager;
+using System.Diagnostics;
namespace LinuxConfigApp
{
internal class LinuxInterop : IInteropFunctions
{
- public LinuxInterop() { }
+ //Different terminal apps possibly installed on a linux system
+ // [0] console executable
+ // [1] argument to set the concole's title
+ // [2] argument to pass a command to be executed to the terminal
+ static readonly string[][] consoleCommands =
+ {
+ new[] {"konsole", "--title", "-e"},
+ new[] {"gnome-terminal", "--title", "--"},
+ new[] {"mate-terminal", "--title", "-x"},
+ new[] {"xterm", "-T", "-e"},
+ };
+
+ public LinuxInterop() { }
public LinuxInterop(params object[] values) { }
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
- public void CopyTextToClipboard(string text) => throw new PlatformNotSupportedException();
- }
+
+ //only run the audo updater is the current app was installed from the
+ //.deb package. Try to detect this by checking if the symlink exists.
+ public bool CanUpdate => Directory.Exists("/usr/bin/libation");
+ public void InstallUpdate(string updateBundle)
+ {
+ RunAsRoot("apt", $"install '{updateBundle}'");
+ }
+
+ public Process RunAsRoot(string exe, string args)
+ {
+ //cribbed this script from VirtualBox's guest additions installer.
+ //It's designed to launch the system's gui superuser password
+ //prompt across multiple distributions and desktop environments.
+ const string runasroot = "/tmp/runasroot.sh";
+ File.WriteAllBytes(runasroot, Properties.Resources.runasroot);
+
+ string command = $"{exe ?? ""} {args ?? ""}".Trim();
+
+ foreach (var console in consoleCommands)
+ {
+ ProcessStartInfo psi = new()
+ {
+ FileName = console[0],
+ UseShellExecute = false,
+ ArgumentList =
+ {
+ console[1],
+ $"Running '{exe}' as root",
+ console[2],
+ "/bin/sh",
+ runasroot,
+ "Installing libation.deb",
+ command,
+ $"Please run '{command}' manually"
+ }
+ };
+
+
+ try
+ {
+ return Process.Start(psi);
+ }
+ catch { }
+ }
+ return null;
+ }
+ }
}
diff --git a/Source/LoadByOS/LinuxConfigApp/Properties/Resources.Designer.cs b/Source/LoadByOS/LinuxConfigApp/Properties/Resources.Designer.cs
new file mode 100644
index 00000000..b48060ab
--- /dev/null
+++ b/Source/LoadByOS/LinuxConfigApp/Properties/Resources.Designer.cs
@@ -0,0 +1,73 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace LinuxConfigApp.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LinuxConfigApp.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] runasroot {
+ get {
+ object obj = ResourceManager.GetObject("runasroot", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+ }
+}
diff --git a/Source/LoadByOS/LinuxConfigApp/Properties/Resources.resx b/Source/LoadByOS/LinuxConfigApp/Properties/Resources.resx
new file mode 100644
index 00000000..ef269a0f
--- /dev/null
+++ b/Source/LoadByOS/LinuxConfigApp/Properties/Resources.resx
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Resources\runasroot.sh;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Source/LoadByOS/LinuxConfigApp/Resources/runasroot.sh b/Source/LoadByOS/LinuxConfigApp/Resources/runasroot.sh
new file mode 100644
index 00000000..5c64d517
--- /dev/null
+++ b/Source/LoadByOS/LinuxConfigApp/Resources/runasroot.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+# $Id: runasroot.sh 153224 2022-08-22 17:43:14Z klaus $
+## @file
+# VirtualBox privileged execution helper script for Linux and Solaris
+#
+
+#
+# Copyright (C) 2009-2022 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY 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 .
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+# Deal with differing "which" semantics
+mywhich() {
+ which "$1" 2>/dev/null | grep -v "no $1"
+}
+
+# Get the name and execute switch for a useful terminal emulator
+#
+# Sets $gxtpath to the emulator path or empty
+# Sets $gxttitle to the "title" switch for that emulator
+# Sets $gxtexec to the "execute" switch for that emulator
+# May clobber $gtx*
+# Calls mywhich
+getxterm() {
+ # gnome-terminal uses -e differently to other emulators
+ for gxti in "konsole --title -e" "gnome-terminal --title -x" "xterm -T -e"; do
+ set $gxti
+ gxtpath="`mywhich $1`"
+ case "$gxtpath" in ?*)
+ gxttitle=$2
+ gxtexec=$3
+ return
+ ;;
+ esac
+ done
+}
+
+# Quotes its argument by inserting '\' in front of every character save
+# for 'A-Za-z0-9/'. Prints the result to stdout.
+quotify() {
+ echo "$1" | sed -e 's/\([^a-zA-Z0-9/]\)/\\\1/g'
+}
+
+ostype=`uname -s`
+if test "$ostype" != "Linux" && test "$ostype" != "SunOS" ; then
+ echo "Linux/Solaris not detected."
+ exit 1
+fi
+
+HAS_TERMINAL=""
+case "$1" in "--has-terminal")
+ shift
+ HAS_TERMINAL="yes"
+ ;;
+esac
+
+case "$#" in "2"|"3")
+ ;;
+ *)
+ echo "Usage: `basename $0` DESCRIPTION COMMAND [ADVICE]" >&2
+ echo >&2
+ echo "Attempt to execute COMMAND with root privileges, displaying DESCRIPTION if" >&2
+ echo "possible and displaying ADVICE if possible if no su(1)-like tool is available." >&2
+ exit 1
+ ;;
+esac
+
+DESCRIPTION=$1
+COMMAND=$2
+ADVICE=$3
+PATH=$PATH:/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/X11/bin
+
+case "$ostype" in SunOS)
+ PATH=$PATH:/usr/sfw/bin:/usr/gnu/bin:/usr/xpg4/bin:/usr/xpg6/bin:/usr/openwin/bin:/usr/ucb
+ GKSU_SWITCHES="-au root"
+ ;;
+ *)
+ GKSU_SWITCHES=""
+ ;;
+esac
+
+case "$HAS_TERMINAL" in "")
+ case "$DISPLAY" in ?*)
+ KDESUDO="`mywhich kdesudo`"
+ case "$KDESUDO" in ?*)
+ eval "`quotify "$KDESUDO"` --comment `quotify "$DESCRIPTION"` -- $COMMAND"
+ exit
+ ;;
+ esac
+
+ KDESU="`mywhich kdesu`"
+ case "$KDESU" in ?*)
+ "$KDESU" -c "$COMMAND"
+ exit
+ ;;
+ esac
+
+ GKSU="`mywhich gksu`"
+ case "$GKSU" in ?*)
+ # Older gksu does not grok --description nor '--' and multiple args.
+ # @todo which versions do?
+ # "$GKSU" --description "$DESCRIPTION" -- "$@"
+ # Note that $GKSU_SWITCHES is NOT quoted in the following
+ "$GKSU" $GKSU_SWITCHES "$COMMAND"
+ exit
+ ;;
+ esac
+ ;;
+ esac # $DISPLAY
+ ;;
+esac # ! $HAS_TERMINAL
+
+# pkexec may work for ssh console sessions as well if the right agents
+# are installed. However it is very generic and does not allow for any
+# custom messages. Thus it comes after gksu.
+## @todo should we insist on either a display or a terminal?
+# case "$DISPLAY$HAS_TERMINAL" in ?*)
+PKEXEC="`mywhich pkexec`"
+case "$PKEXEC" in ?*)
+ eval "\"$PKEXEC\" $COMMAND"
+ exit
+ ;;
+esac
+# ;;S
+#esac
+
+case "$HAS_TERMINAL" in ?*)
+ USE_SUDO=
+ grep -q Ubuntu /etc/lsb-release 2>/dev/null && USE_SUDO=true
+ # On Ubuntu we need sudo instead of su. Assume this works, and is only
+ # needed for Ubuntu until proven wrong.
+ case $USE_SUDO in true)
+ SUDO_COMMAND="`quotify "$SUDO"` -- $COMMAND"
+ eval "$SUDO_COMMAND"
+ exit
+ ;;
+ esac
+
+ SU="`mywhich su`"
+ case "$SU" in ?*)
+ "$SU" - root -c "$COMMAND"
+ exit
+ ;;
+ esac
+ ;;
+esac
+
+# The ultimate fallback is running 'su -' within an xterm. We use the
+# title of the xterm to tell what is going on.
+case "$DISPLAY" in ?*)
+ SU="`mywhich su`"
+ case "$SU" in ?*)
+ getxterm
+ case "$gxtpath" in ?*)
+ "$gxtpath" "$gxttitle" "$DESCRIPTION - su" "$gxtexec" su - root -c "$COMMAND"
+ exit
+ ;;
+ esac
+ esac
+esac # $DISPLAY
+
+# Failure...
+case "$DISPLAY" in ?*)
+ echo "Unable to locate 'pkexec', 'gksu' or 'su+xterm'. $ADVICE" >&2
+ ;;
+ *)
+ echo "Unable to locate 'pkexec'. $ADVICE" >&2
+ ;;
+esac
+
+exit 1
diff --git a/Source/LibationAvalonia/glass-with-glow_256.svg b/Source/LoadByOS/LinuxConfigApp/glass-with-glow_256.svg
similarity index 100%
rename from Source/LibationAvalonia/glass-with-glow_256.svg
rename to Source/LoadByOS/LinuxConfigApp/glass-with-glow_256.svg
diff --git a/Source/LibationAvalonia/Info.plist b/Source/LoadByOS/MacOSConfigApp/Info.plist
similarity index 87%
rename from Source/LibationAvalonia/Info.plist
rename to Source/LoadByOS/MacOSConfigApp/Info.plist
index c88f1c80..90cbcaf1 100644
--- a/Source/LibationAvalonia/Info.plist
+++ b/Source/LoadByOS/MacOSConfigApp/Info.plist
@@ -15,6 +15,8 @@
libation.icns
CFBundleVersion
VERSION_STRING
+ com.apple.security.app-sandbox
+
com.apple.security.cs.allow-jit
com.apple.security.cs.allow-unsigned-executable-memory
@@ -23,5 +25,7 @@
com.apple.security.cs.disable-executable-page-protection
+ com.apple.security.automation.apple-events
+
\ No newline at end of file
diff --git a/Source/LoadByOS/MacOSConfigApp/MacOSConfigApp.csproj b/Source/LoadByOS/MacOSConfigApp/MacOSConfigApp.csproj
index 55bb69bf..865067f1 100644
--- a/Source/LoadByOS/MacOSConfigApp/MacOSConfigApp.csproj
+++ b/Source/LoadByOS/MacOSConfigApp/MacOSConfigApp.csproj
@@ -34,4 +34,13 @@
+
+
+ Always
+
+
+ Always
+
+
+
\ No newline at end of file
diff --git a/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs b/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs
index 378e7323..8bf6f9a3 100644
--- a/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs
+++ b/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs
@@ -1,14 +1,78 @@
using LibationFileManager;
+using System.Diagnostics;
namespace MacOSConfigApp
{
internal class MacOSInterop : IInteropFunctions
- {
- public MacOSInterop() { }
+ {
+ private const string AppPath = "/Applications/Libation.app";
+ public MacOSInterop() { }
public MacOSInterop(params object[] values) { }
- public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
+ public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
- public void CopyTextToClipboard(string text) => throw new PlatformNotSupportedException();
- }
+
+ //I haven't figured out how to find the app bundle's directory from within
+ //the running process, so don't update unless it's "installed" in /Applications
+ public bool CanUpdate => Directory.Exists(AppPath);
+
+ public void InstallUpdate(string updateBundle)
+ {
+ Serilog.Log.Information($"Extracting update bundle to {AppPath}");
+
+ //tar wil overwrite existing without elevated privileges
+ Process.Start("tar", $"-xzf \"{updateBundle}\" -C \"/Applications\"").WaitForExit();
+
+ //For now, it seems like this step is unnecessary. We can overwrite and
+ //run Libation without needing to re-add the exception. This is insurance.
+ RunAsRoot(null, $"""
+sudo spctl --master-disable
+sudo spctl --add --label 'Libation' {AppPath}
+open {AppPath}
+sudo spctl --master-enable
+""");
+ }
+
+ //Using osascript -e '[script]' works from the terminal, but I haven't figured
+ //out the syntax for it to work from create_process, so write to stdin instead.
+ public Process RunAsRoot(string _, string command)
+ {
+ const string osascript = "osascript";
+ var fullCommand = $"do shell script \"{command}\" with administrator privileges";
+
+ var psi = new ProcessStartInfo()
+ {
+ FileName = osascript,
+ UseShellExecute = false,
+ Arguments = "-",
+ RedirectStandardError= true,
+ RedirectStandardOutput= true,
+ RedirectStandardInput= true,
+ };
+
+ Serilog.Log.Logger.Information($"running {osascript} as root: {{script}}", fullCommand);
+
+ var proc = Process.Start(psi);
+ proc.ErrorDataReceived += Proc_ErrorDataReceived;
+ proc.OutputDataReceived += Proc_OutputDataReceived;
+ proc.BeginErrorReadLine();
+ proc.BeginOutputReadLine();
+ proc.StandardInput.WriteLine(fullCommand);
+ proc.StandardInput.Close();
+
+ return proc;
+ }
+
+ private void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ if (e.Data != null)
+ Serilog.Log.Logger.Information("stderr: {data}", e.Data);
+ }
+
+ private void Proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ if (e.Data!= null)
+ Serilog.Log.Logger.Information("stderr: {data}", e.Data);
+ }
+ }
}
diff --git a/Source/LibationAvalonia/libation.icns b/Source/LoadByOS/MacOSConfigApp/libation.icns
similarity index 100%
rename from Source/LibationAvalonia/libation.icns
rename to Source/LoadByOS/MacOSConfigApp/libation.icns
diff --git a/Source/LoadByOS/WindowsConfigApp/WinInterop.cs b/Source/LoadByOS/WindowsConfigApp/WinInterop.cs
index ff4ef910..42cded34 100644
--- a/Source/LoadByOS/WindowsConfigApp/WinInterop.cs
+++ b/Source/LoadByOS/WindowsConfigApp/WinInterop.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -35,8 +36,31 @@ namespace WindowsConfigApp
public void DeleteFolderIcon(string directory)
=> new DirectoryInfo(directory)?.DeleteIcon();
+ public bool CanUpdate => true;
+ public void InstallUpdate(string updateBundle)
+ {
+ var thisExe = Environment.ProcessPath;
+ var thisDir = Path.GetDirectoryName(thisExe);
+ var zipExtractor = Path.Combine(Path.GetTempPath(), "ZipExtractor.exe");
- public void CopyTextToClipboard(string text)
- => Clipboard.SetText(text);
+ File.Copy("ZipExtractor.exe", zipExtractor, overwrite: true);
+
+ RunAsRoot(zipExtractor, $"--input \"{updateBundle}\" --output \"{thisDir}\" --executable \"{thisExe}\"");
+ }
+
+ public Process RunAsRoot(string exe, string args)
+ {
+ var psi = new ProcessStartInfo()
+ {
+ FileName = exe,
+ UseShellExecute = true,
+ Verb = "runas",
+ WindowStyle = ProcessWindowStyle.Normal,
+ CreateNoWindow = true,
+ Arguments = args
+ };
+
+ return Process.Start(psi);
+ }
}
}
diff --git a/Source/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj b/Source/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj
index 5e37bedb..0dba0cbf 100644
--- a/Source/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj
+++ b/Source/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj
@@ -40,4 +40,10 @@
+
+
+ Always
+
+
+
\ No newline at end of file
diff --git a/Source/LibationAvalonia/ZipExtractor.exe b/Source/LoadByOS/WindowsConfigApp/ZipExtractor.exe
similarity index 100%
rename from Source/LibationAvalonia/ZipExtractor.exe
rename to Source/LoadByOS/WindowsConfigApp/ZipExtractor.exe