| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 | 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;using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;namespace IsoMounter{    public class LinuxIsoManager : IIsoMounter    {        [DllImport("libc", SetLastError = true)]        static extern uint getuid();        #region Private Fields        private readonly bool ExecutablesAvailable;        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, IProcessFactory processFactory)        {            _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 (OperatingSystem.Id != OperatingSystemId.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),                OperatingSystem.Name,                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    }}
 |