123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- using System;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Threading;
- using System.Threading.Tasks;
- using MediaBrowser.Model.Diagnostics;
- using MediaBrowser.Model.IO;
- using MediaBrowser.Model.System;
- using Microsoft.Extensions.Logging;
- namespace IsoMounter
- {
- public class LinuxIsoManager : IIsoMounter
- {
- [DllImport("libc", SetLastError = true)]
- 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 = Path.DirectorySeparatorChar + "tmp" + Path.DirectorySeparatorChar + "Emby";
- _logger.LogDebug(
- "[{0}] System PATH is currently set to [{1}].",
- Name,
- Environment.GetEnvironmentVariable("PATH") ?? ""
- );
- _logger.LogDebug(
- "[{0}] System path separator is [{1}].",
- Name,
- Path.PathSeparator
- );
- _logger.LogDebug(
- "[{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.LogInformation(
- "[{0}] Using version of [sudo] located at [{1}].",
- Name,
- SudoCommand
- );
- MountCommand = GetFullPathForExecutable("mount");
- _logger.LogInformation(
- "[{0}] Using version of [mount] located at [{1}].",
- Name,
- MountCommand
- );
- UmountCommand = GetFullPathForExecutable("umount");
- _logger.LogInformation(
- "[{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 => true;
- public string Name => "LinuxMount";
- public bool RequiresInstallation => false;
- public bool CanMount(string path)
- {
- if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Linux)
- {
- return false;
- }
- _logger.LogInformation(
- "[{0}] Checking we can attempt to mount [{1}], Extension = [{2}], Operating System = [{3}], Executables Available = [{4}].",
- Name,
- path,
- Path.GetExtension(path),
- EnvironmentInfo.OperatingSystem,
- ExecutablesAvailable
- );
- if (ExecutablesAvailable)
- {
- return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase);
- }
- else
- {
- return false;
- }
- }
- public Task Install(CancellationToken cancellationToken)
- {
- return Task.FromResult(false);
- }
- public Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken)
- {
- if (MountISO(isoPath, out LinuxMount mountedISO))
- {
- return Task.FromResult<IIsoMount>(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.LogInformation(
- "[{0}] Disposing [{1}].",
- Name,
- disposing
- );
- 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 (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator))
- {
- string path = test.Trim();
- if (!string.IsNullOrEmpty(path) && File.Exists(path = Path.Combine(path, name)))
- {
- return Path.GetFullPath(path);
- }
- }
- return string.Empty;
- }
- private uint GetUID()
- {
- var uid = getuid();
- _logger.LogDebug(
- "[{0}] GetUserId() returned [{2}].",
- Name,
- 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.LogDebug(
- "[{Name}] Standard output from process is [{Error}].",
- Name,
- process.StandardOutput.ReadToEnd()
- );
- _logger.LogDebug(
- "[{Name}] Standard error from process is [{Error}].",
- Name,
- process.StandardError.ReadToEnd()
- );
- }
- catch (Exception ex)
- {
- processFailed = true;
- _logger.LogDebug(ex, "[{Name}] Unhandled exception executing command.", Name);
- }
- 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.LogInformation(
- "[{Name}] Attempting to mount [{Path}].",
- Name,
- isoPath
- );
- _logger.LogDebug(
- "[{Name}] ISO will be mounted at [{Path}].",
- Name,
- mountPoint
- );
- }
- else
- {
- throw new ArgumentNullException(nameof(isoPath));
- }
- try
- {
- Directory.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.LogDebug(
- "[{0}] Mount command [{1}], mount arguments [{2}].",
- Name,
- cmdFilename,
- cmdArguments
- );
- if (ExecuteCommand(cmdFilename, cmdArguments))
- {
- _logger.LogInformation(
- "[{0}] ISO mount completed successfully.",
- Name
- );
- mountedISO = new LinuxMount(this, isoPath, mountPoint);
- }
- else
- {
- _logger.LogInformation(
- "[{0}] ISO mount completed with errors.",
- Name
- );
- try
- {
- Directory.Delete(mountPoint, false);
- }
- catch (Exception ex)
- {
- _logger.LogInformation(ex, "[{Name}] Unhandled exception removing mount point.", Name);
- }
- mountedISO = null;
- }
- return mountedISO != null;
- }
- private void UnmountISO(LinuxMount mount)
- {
- string cmdArguments;
- string cmdFilename;
- if (mount != null)
- {
- _logger.LogInformation(
- "[{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.LogDebug(
- "[{0}] Umount command [{1}], umount arguments [{2}].",
- Name,
- cmdFilename,
- cmdArguments
- );
- if (ExecuteCommand(cmdFilename, cmdArguments))
- {
- _logger.LogInformation(
- "[{0}] ISO unmount completed successfully.",
- Name
- );
- }
- else
- {
- _logger.LogInformation(
- "[{0}] ISO unmount completed with errors.",
- Name
- );
- }
- try
- {
- Directory.Delete(mount.MountedPath, false);
- }
- catch (Exception ex)
- {
- _logger.LogInformation(ex, "[{Name}] Unhandled exception removing mount point.", Name);
- }
- }
- #endregion
- #region Internal Methods
- internal void OnUnmount(LinuxMount mount)
- {
- UnmountISO(mount);
- }
- #endregion
- }
- }
|