aboutsummaryrefslogtreecommitdiff
path: root/Emby.IsoMounting/IsoMounter
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.IsoMounting/IsoMounter')
-rw-r--r--Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs8
-rw-r--r--Emby.IsoMounting/IsoMounter/IsoMounter.csproj13
-rw-r--r--Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs481
-rw-r--r--Emby.IsoMounting/IsoMounter/LinuxMount.cs85
-rw-r--r--Emby.IsoMounting/IsoMounter/Plugin.cs42
5 files changed, 629 insertions, 0 deletions
diff --git a/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs b/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs
new file mode 100644
index 0000000000..9308b8c891
--- /dev/null
+++ b/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs
@@ -0,0 +1,8 @@
+using MediaBrowser.Model.Plugins;
+
+namespace IsoMounter.Configuration
+{
+ public class PluginConfiguration : BasePluginConfiguration
+ {
+ }
+} \ No newline at end of file
diff --git a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj
new file mode 100644
index 0000000000..49a9d948c0
--- /dev/null
+++ b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs
new file mode 100644
index 0000000000..12669d05d3
--- /dev/null
+++ b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs
@@ -0,0 +1,481 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+using System.Runtime.InteropServices;
+
+namespace IsoMounter
+{
+ public class LinuxIsoManager : IIsoMounter
+ {
+ [DllImport("libc", SetLastError = true)]
+ public static extern uint getuid();
+
+ #region Private Fields
+
+ private readonly IEnvironmentInfo EnvironmentInfo;
+ private readonly bool ExecutablesAvailable;
+ private readonly IFileSystem FileSystem;
+ private readonly ILogger Logger;
+ private readonly string MountCommand;
+ private readonly string MountPointRoot;
+ private readonly IProcessFactory ProcessFactory;
+ private readonly string SudoCommand;
+ private readonly string UmountCommand;
+
+ #endregion
+
+ #region Constructor(s)
+
+ public LinuxIsoManager(ILogger logger, IFileSystem fileSystem, IEnvironmentInfo environment, IProcessFactory processFactory)
+ {
+
+ EnvironmentInfo = environment;
+ FileSystem = fileSystem;
+ Logger = logger;
+ ProcessFactory = processFactory;
+
+ MountPointRoot = FileSystem.DirectorySeparatorChar + "tmp" + FileSystem.DirectorySeparatorChar + "Emby";
+
+ Logger.Debug(
+ "[{0}] System PATH is currently set to [{1}].",
+ Name,
+ EnvironmentInfo.GetEnvironmentVariable("PATH") ?? ""
+ );
+
+ Logger.Debug(
+ "[{0}] System path separator is [{1}].",
+ Name,
+ EnvironmentInfo.PathSeparator
+ );
+
+ Logger.Debug(
+ "[{0}] Mount point root is [{1}].",
+ Name,
+ MountPointRoot
+ );
+
+ //
+ // Get the location of the executables we need to support mounting/unmounting ISO images.
+ //
+
+ SudoCommand = GetFullPathForExecutable("sudo");
+
+ Logger.Info(
+ "[{0}] Using version of [sudo] located at [{1}].",
+ Name,
+ SudoCommand
+ );
+
+ MountCommand = GetFullPathForExecutable("mount");
+
+ Logger.Info(
+ "[{0}] Using version of [mount] located at [{1}].",
+ Name,
+ MountCommand
+ );
+
+ UmountCommand = GetFullPathForExecutable("umount");
+
+ Logger.Info(
+ "[{0}] Using version of [umount] located at [{1}].",
+ Name,
+ UmountCommand
+ );
+
+ if (!String.IsNullOrEmpty(SudoCommand) && !String.IsNullOrEmpty(MountCommand) && !String.IsNullOrEmpty(UmountCommand)) {
+ ExecutablesAvailable = true;
+ } else {
+ ExecutablesAvailable = false;
+ }
+
+ }
+
+ #endregion
+
+ #region Interface Implementation for IIsoMounter
+
+ public bool IsInstalled {
+ get {
+ return true;
+ }
+ }
+
+ public string Name {
+ get { return "LinuxMount"; }
+ }
+
+ public bool RequiresInstallation {
+ get {
+ return false;
+ }
+ }
+
+ public bool CanMount(string path)
+ {
+
+ if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Linux) {
+ Logger.Info(
+ "[{0}] Checking we can attempt to mount [{1}], Extension = [{2}], Operating System = [{3}], Executables Available = [{4}].",
+ Name,
+ path,
+ Path.GetExtension(path),
+ EnvironmentInfo.OperatingSystem,
+ ExecutablesAvailable.ToString()
+ );
+
+ if (ExecutablesAvailable) {
+ return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase);
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ }
+
+ public Task Install(CancellationToken cancellationToken)
+ {
+ return Task.FromResult(false);
+ }
+
+ public async Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken)
+ {
+
+ LinuxMount mountedISO;
+
+ if (MountISO(isoPath, out mountedISO)) {
+
+ return mountedISO;
+
+ }else{
+
+ throw new IOException(String.Format(
+ "An error occurred trying to mount image [$0].",
+ isoPath
+ ));
+
+ }
+
+ }
+
+ #endregion
+
+ #region Interface Implementation for IDisposable
+
+ // Flag: Has Dispose already been called?
+ private bool disposed = false;
+
+ public void Dispose()
+ {
+
+ // Dispose of unmanaged resources.
+ Dispose(true);
+
+ // Suppress finalization.
+ GC.SuppressFinalize(this);
+
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+
+ if (disposed) {
+ return;
+ }
+
+ Logger.Info(
+ "[{0}] Disposing [{1}].",
+ Name,
+ disposing.ToString()
+ );
+
+ if (disposing) {
+
+ //
+ // Free managed objects here.
+ //
+
+ }
+
+ //
+ // Free any unmanaged objects here.
+ //
+
+ disposed = true;
+
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private string GetFullPathForExecutable(string name)
+ {
+
+ foreach (string test in (EnvironmentInfo.GetEnvironmentVariable("PATH") ?? "").Split(EnvironmentInfo.PathSeparator)) {
+
+ string path = test.Trim();
+
+ if (!String.IsNullOrEmpty(path) && FileSystem.FileExists(path = Path.Combine(path, name))) {
+ return FileSystem.GetFullPath(path);
+ }
+
+ }
+
+ return String.Empty;
+
+ }
+
+ private uint GetUID()
+ {
+
+ var uid = getuid();
+
+ Logger.Debug(
+ "[{0}] Our current UID is [{1}], GetUserId() returned [{2}].",
+ Name,
+ uid.ToString(),
+ uid
+ );
+
+ return uid;
+
+ }
+
+ private bool ExecuteCommand(string cmdFilename, string cmdArguments)
+ {
+
+ bool processFailed = false;
+
+ var process = ProcessFactory.Create(
+ new ProcessOptions {
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ FileName = cmdFilename,
+ Arguments = cmdArguments,
+ IsHidden = true,
+ ErrorDialog = false,
+ EnableRaisingEvents = true
+ }
+ );
+
+ try {
+
+ process.Start();
+
+ //StreamReader outputReader = process.StandardOutput.;
+ //StreamReader errorReader = process.StandardError;
+
+ Logger.Debug(
+ "[{0}] Standard output from process is [{1}].",
+ Name,
+ process.StandardOutput.ReadToEnd()
+ );
+
+ Logger.Debug(
+ "[{0}] Standard error from process is [{1}].",
+ Name,
+ process.StandardError.ReadToEnd()
+ );
+
+ } catch (Exception ex) {
+
+ processFailed = true;
+
+ Logger.Debug(
+ "[{0}] Unhandled exception executing command, exception is [{1}].",
+ Name,
+ ex.Message
+ );
+
+ }
+
+ if (!processFailed && process.ExitCode == 0) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ private bool MountISO(string isoPath, out LinuxMount mountedISO)
+ {
+
+ string cmdArguments;
+ string cmdFilename;
+ string mountPoint = Path.Combine(MountPointRoot, Guid.NewGuid().ToString());
+
+ if (!string.IsNullOrEmpty(isoPath)) {
+
+ Logger.Info(
+ "[{0}] Attempting to mount [{1}].",
+ Name,
+ isoPath
+ );
+
+ Logger.Debug(
+ "[{0}] ISO will be mounted at [{1}].",
+ Name,
+ mountPoint
+ );
+
+ } else {
+
+ throw new ArgumentNullException(nameof(isoPath));
+
+ }
+
+ try {
+ FileSystem.CreateDirectory(mountPoint);
+ } catch (UnauthorizedAccessException) {
+ throw new IOException("Unable to create mount point(Permission denied) for " + isoPath);
+ } catch (Exception) {
+ throw new IOException("Unable to create mount point for " + isoPath);
+ }
+
+ if (GetUID() == 0) {
+ cmdFilename = MountCommand;
+ cmdArguments = string.Format("\"{0}\" \"{1}\"", isoPath, mountPoint);
+ } else {
+ cmdFilename = SudoCommand;
+ cmdArguments = string.Format("\"{0}\" \"{1}\" \"{2}\"", MountCommand, isoPath, mountPoint);
+ }
+
+ Logger.Debug(
+ "[{0}] Mount command [{1}], mount arguments [{2}].",
+ Name,
+ cmdFilename,
+ cmdArguments
+ );
+
+ if (ExecuteCommand(cmdFilename, cmdArguments)) {
+
+ Logger.Info(
+ "[{0}] ISO mount completed successfully.",
+ Name
+ );
+
+ mountedISO = new LinuxMount(this, isoPath, mountPoint);
+
+ } else {
+
+ Logger.Info(
+ "[{0}] ISO mount completed with errors.",
+ Name
+ );
+
+ try {
+
+ FileSystem.DeleteDirectory(mountPoint, false);
+
+ } catch (Exception ex) {
+
+ Logger.Info(
+ "[{0}] Unhandled exception removing mount point, exception is [{1}].",
+ Name,
+ ex.Message
+ );
+
+ }
+
+ mountedISO = null;
+
+ }
+
+ return mountedISO != null;
+
+ }
+
+ private void UnmountISO(LinuxMount mount)
+ {
+
+ string cmdArguments;
+ string cmdFilename;
+
+ if (mount != null) {
+
+ Logger.Info(
+ "[{0}] Attempting to unmount ISO [{1}] mounted on [{2}].",
+ Name,
+ mount.IsoPath,
+ mount.MountedPath
+ );
+
+ } else {
+
+ throw new ArgumentNullException(nameof(mount));
+
+ }
+
+ if (GetUID() == 0) {
+ cmdFilename = UmountCommand;
+ cmdArguments = string.Format("\"{0}\"", mount.MountedPath);
+ } else {
+ cmdFilename = SudoCommand;
+ cmdArguments = string.Format("\"{0}\" \"{1}\"", UmountCommand, mount.MountedPath);
+ }
+
+ Logger.Debug(
+ "[{0}] Umount command [{1}], umount arguments [{2}].",
+ Name,
+ cmdFilename,
+ cmdArguments
+ );
+
+ if (ExecuteCommand(cmdFilename, cmdArguments)) {
+
+ Logger.Info(
+ "[{0}] ISO unmount completed successfully.",
+ Name
+ );
+
+ } else {
+
+ Logger.Info(
+ "[{0}] ISO unmount completed with errors.",
+ Name
+ );
+
+ }
+
+ try {
+
+ FileSystem.DeleteDirectory(mount.MountedPath, false);
+
+ } catch (Exception ex) {
+
+ Logger.Info(
+ "[{0}] Unhandled exception removing mount point, exception is [{1}].",
+ Name,
+ ex.Message
+ );
+
+ }
+
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ internal void OnUnmount(LinuxMount mount)
+ {
+
+ UnmountISO(mount);
+
+ }
+
+ #endregion
+
+ }
+
+}
+
diff --git a/Emby.IsoMounting/IsoMounter/LinuxMount.cs b/Emby.IsoMounting/IsoMounter/LinuxMount.cs
new file mode 100644
index 0000000000..8d1f56e395
--- /dev/null
+++ b/Emby.IsoMounting/IsoMounter/LinuxMount.cs
@@ -0,0 +1,85 @@
+using System;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+
+namespace IsoMounter
+{
+ internal class LinuxMount : IIsoMount
+ {
+
+ #region Private Fields
+
+ private readonly LinuxIsoManager linuxIsoManager;
+
+ #endregion
+
+ #region Constructor(s)
+
+ internal LinuxMount(LinuxIsoManager isoManager, string isoPath, string mountFolder)
+ {
+
+ linuxIsoManager = isoManager;
+
+ IsoPath = isoPath;
+ MountedPath = mountFolder;
+
+ }
+
+ #endregion
+
+ #region Interface Implementation for IDisposable
+
+ // Flag: Has Dispose already been called?
+ private bool disposed = false;
+
+ public void Dispose()
+ {
+
+ // Dispose of unmanaged resources.
+ Dispose(true);
+
+ // Suppress finalization.
+ GC.SuppressFinalize(this);
+
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+
+ if (disposed) {
+ return;
+ }
+
+ if (disposing) {
+
+ //
+ // Free managed objects here.
+ //
+
+ linuxIsoManager.OnUnmount(this);
+
+ }
+
+ //
+ // Free any unmanaged objects here.
+ //
+
+ disposed = true;
+
+ }
+
+ #endregion
+
+ #region Interface Implementation for IIsoMount
+
+ public string IsoPath { get; private set; }
+ public string MountedPath { get; private set; }
+
+ #endregion
+
+ }
+
+}
+
diff --git a/Emby.IsoMounting/IsoMounter/Plugin.cs b/Emby.IsoMounting/IsoMounter/Plugin.cs
new file mode 100644
index 0000000000..ea53175024
--- /dev/null
+++ b/Emby.IsoMounting/IsoMounter/Plugin.cs
@@ -0,0 +1,42 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Serialization;
+using System;
+using IsoMounter.Configuration;
+
+namespace IsoMounter
+{
+ public class Plugin : BasePlugin<PluginConfiguration>
+ {
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) : base(applicationPaths, xmlSerializer)
+ {
+ }
+
+ private Guid _id = new Guid("4682DD4C-A675-4F1B-8E7C-79ADF137A8F8");
+ public override Guid Id
+ {
+ get { return _id; }
+ }
+
+ /// <summary>
+ /// Gets the name of the plugin
+ /// </summary>
+ /// <value>The name.</value>
+ public override string Name
+ {
+ get { return "Iso Mounter"; }
+ }
+
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ /// <value>The description.</value>
+ public override string Description
+ {
+ get
+ {
+ return "Mount and stream ISO contents";
+ }
+ }
+ }
+}