Browse Source

Remove DBus code. Use common linux kernel interfaces

for querying volume mounts.
pull/2777/head
Jumar Macato 7 years ago
parent
commit
7efaa96674
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 12
      src/Avalonia.Controls/Platform/MountedDriveInfo.cs
  2. 96
      src/Avalonia.Dialogs/ManagedFileChooserSources.cs
  3. 1
      src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
  4. 248
      src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs
  5. 2
      src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoProvider.cs
  6. 1395
      src/Avalonia.FreeDesktop/UDisks2.cs

12
src/Avalonia.Controls/Platform/MountedDriveInfo.cs

@ -11,18 +11,14 @@ namespace Avalonia.Controls.Platform
public class MountedVolumeInfo : IEquatable<MountedVolumeInfo>
{
public string VolumeLabel { get; set; }
public string VolumeName { get; set; }
public string VolumePath { get; set; }
public ulong VolumeSizeBytes { get; set; }
public string DevicePath { get; set; }
public string MountPath { get; set; }
public bool Equals(MountedVolumeInfo other)
{
return this.VolumeLabel.Equals(other.VolumeLabel) &&
this.VolumeName.Equals(other.VolumeName) &&
this.VolumeSizeBytes.Equals(other.VolumeSizeBytes) &&
this.DevicePath.Equals(other.DevicePath) &&
this.MountPath.Equals(other.MountPath);
return this.VolumeSizeBytes.Equals(other.VolumeSizeBytes) &&
this.VolumePath.Equals(other.VolumePath) &&
(this.VolumeLabel ?? string.Empty).Equals(other.VolumeLabel ?? string.Empty);
}
}
}

96
src/Avalonia.Dialogs/ManagedFileChooserSources.cs

@ -53,56 +53,64 @@ namespace Avalonia.Dialogs
public static ManagedFileChooserNavigationItem[] DefaultGetFileSystemRoots()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return DriveInfo.GetDrives().Select(d => new ManagedFileChooserNavigationItem
{
ItemType = ManagedFileChooserItemType.Volume,
DisplayName = d.Name,
Path = d.RootDirectory.FullName
}).ToArray();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var paths = Directory.GetDirectories("/Volumes");
// if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
// {
// return DriveInfo.GetDrives().Select(d => new ManagedFileChooserNavigationItem
// {
// ItemType = ManagedFileChooserItemType.Volume,
// DisplayName = d.Name,
// Path = d.RootDirectory.FullName
// }).ToArray();
// }
// else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
// {
// var paths = Directory.GetDirectories("/Volumes");
return paths.Select(x => new ManagedFileChooserNavigationItem
{
ItemType = ManagedFileChooserItemType.Volume,
DisplayName = Path.GetFileName(x),
Path = x
}).ToArray();
}
else
{
return MountedVolumes
.Where(x => !x.MountPath.StartsWith("/boot"))
.Select(x =>
// return paths.Select(x => new ManagedFileChooserNavigationItem
// {
// ItemType = ManagedFileChooserItemType.Volume,
// DisplayName = Path.GetFileName(x),
// Path = x
// }).ToArray();
// }
// else
// {
return MountedVolumes
.Select(x =>
{
var displayName = x.VolumeLabel;
if (displayName == null)
{
if (x.MountPath == "/")
if (x.VolumePath == "/")
{
return new ManagedFileChooserNavigationItem
{
ItemType = ManagedFileChooserItemType.Volume,
DisplayName = "File System",
Path = "/"
};
displayName = "File System";
}
else
else if (x.VolumeSizeBytes > 0)
{
var dNameEmpty = string.IsNullOrEmpty(x.VolumeLabel.Trim());
displayName = $"{ByteSizeHelper.ToString(x.VolumeSizeBytes)} Volume";
};
}
return new ManagedFileChooserNavigationItem
{
ItemType = ManagedFileChooserItemType.Volume,
DisplayName = dNameEmpty ? $"{ByteSizeHelper.ToString(x.VolumeSizeBytes)} Volume"
: x.VolumeLabel,
Path = x.MountPath
};
}
})
.ToArray();
}
try
{
Directory.GetFiles(x.VolumePath);
}
catch (UnauthorizedAccessException _)
{
return null;
}
return new ManagedFileChooserNavigationItem
{
ItemType = ManagedFileChooserItemType.Volume,
DisplayName = displayName,
Path = x.VolumePath
};
})
.Where(x => x != null)
.ToArray();
// }
}
}
}

1
src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj

@ -6,7 +6,6 @@
<ItemGroup>
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<PackageReference Include="Tmds.DBus" Version="0.7.0" />
</ItemGroup>
</Project>

248
src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs

@ -3,200 +3,112 @@ using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Avalonia.Controls.Platform;
using Tmds.DBus;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.Text;
namespace Avalonia.FreeDesktop.Dbus
{
public partial class LinuxMountedVolumeInfoProvider
internal class LinuxMountedVolumeInfoListener : IDisposable
{
private class LinuxMountedVolumeInfoListener : IDisposable
{
private CompositeDisposable _disposables;
private readonly Connection _sysDbus;
private readonly IObjectManager _udisk2Manager;
private readonly ObservableCollection<MountedVolumeInfo> _targetObs;
private bool disposedValue = false;
public LinuxMountedVolumeInfoListener(ref ObservableCollection<MountedVolumeInfo> target)
{
_disposables = new CompositeDisposable();
this._targetObs = target;
try
{
this._sysDbus = Connection.System;
this._udisk2Manager = _sysDbus.CreateProxy<IObjectManager>("org.freedesktop.UDisks2", "/org/freedesktop/UDisks2");
// Test if DBus works.
var _ = _udisk2Manager.GetManagedObjectsAsync().Result;
StartDbus();
}
catch (Exception ex)
{
Logging.Logger.Warning("Linux Volume Listener", this,
"Exception while initializing DBus connection: {0}; {1}"
, ex.Message, ex.StackTrace);
StartFallback();
}
}
private void StartFallback()
{
var pollTimer = Observable.Interval(TimeSpan.FromSeconds(1))
.Subscribe(PollFallback);
_disposables.Add(pollTimer);
PollFallback(0);
}
private const string DevByLabelDir = "/dev/disk/by-label/";
private const string ProcPartitionsDir = "/proc/partitions";
private const string ProcMountsDir = "/proc/mounts";
private void PollFallback(long _)
{
}
[DllImport("libc", SetLastError = true)]
private static extern long readlink([MarshalAs(UnmanagedType.LPArray)]
byte[] filename, [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, long buflen);
private async void PollDbus()
{
var newDriveList = new List<MountedVolumeInfo>();
var fProcMounts = File.ReadAllLines("/proc/mounts");
private string ReadLink(string path)
{
var symlink = Encoding.UTF8.GetBytes(path);
var results = new byte[4095];
readlink(symlink, results, results.Length);
var rawstr = Encoding.UTF8.GetString(results);
return rawstr.Substring(0, rawstr.IndexOf('\0')); ;
}
var managedObj = await _udisk2Manager.GetManagedObjectsAsync();
private CompositeDisposable _disposables;
private ObservableCollection<MountedVolumeInfo> _targetObs;
private bool _beenDisposed = false;
var res_drives = managedObj.Where(x => x.Key.ToString().Contains("/org/freedesktop/UDisks2/drives/"))
.Select(x => x.Key);
public LinuxMountedVolumeInfoListener(ref ObservableCollection<MountedVolumeInfo> target)
{
_disposables = new CompositeDisposable();
this._targetObs = target;
var res_blockdev = managedObj.Where(x => x.Key.ToString().Contains("/org/freedesktop/UDisks2/block_devices/"))
.Select(x => x);
var pollTimer = Observable.Interval(TimeSpan.FromSeconds(2))
.Subscribe(Poll);
var res_fs = managedObj.Where(x => x.Key.ToString().Contains("system"))
.Select(x => x.Key)
.ToList();
_disposables.Add(pollTimer);
foreach (var block in res_blockdev)
{
try
{
var iblock = _sysDbus.CreateProxy<IBlock>("org.freedesktop.UDisks2", block.Key);
var iblockProps = await iblock.GetAllAsync();
var block_drive = await iblock.GetDriveAsync();
if (!res_drives.Contains(block_drive)) continue;
var drive_key = res_drives.Single(x => x == block_drive);
var drives = _sysDbus.CreateProxy<IDrive>("org.freedesktop.UDisks2", drive_key);
var drivesProps = await drives.GetAllAsync();
var devRawBytes = iblockProps.Device.Take(iblockProps.Device.Length - 1).ToArray();
var devPath = System.Text.Encoding.UTF8.GetString(devRawBytes);
var blockLabel = iblockProps.IdLabel;
var blockSize = iblockProps.Size;
var driveName = drivesProps.Id;
// HACK: There should be something in udisks2 to
// get this data but I have no idea where.
var mountPoint = fProcMounts.Select(x => x.Split(' '))
.Where(x => x[0] == devPath)
.Select(x => x[1])
.SingleOrDefault();
if (mountPoint is null) continue;
var k = new MountedVolumeInfo()
{
VolumeLabel = blockLabel,
VolumeName = driveName,
VolumeSizeBytes = blockSize,
DevicePath = devPath,
MountPath = mountPoint
};
newDriveList.Add(k);
}
catch (Exception ex)
{
Logging.Logger.Warning("Linux Volume Listener", this, "Exception while enumerating DBus items: {0}; {1}"
, ex.Message, ex.StackTrace);
}
}
Poll(0);
}
UpdateCollection(_targetObs, newDriveList);
}
private string GetSymlinkTarget(string x) => Path.GetFullPath(Path.Combine(DevByLabelDir, ReadLink(x)));
private async void StartDbus()
private void Poll(long _)
{
var fProcPartitions = File.ReadAllLines(ProcPartitionsDir)
.Skip(1)
.Where(p => !string.IsNullOrEmpty(p))
.Select(p => Regex.Replace(p, @"\s{2,}", " ").Trim().Split(' '))
.Select(p => (p[2].Trim(), p[3].Trim()))
.Select(p => (Convert.ToUInt64(p.Item1) * 1024, "/dev/" + p.Item2));
var fProcMounts = File.ReadAllLines(ProcMountsDir)
.Select(x => x.Split(' '))
.Select(x => (x[0], x[1]));
var labelDirEnum = Directory.Exists(DevByLabelDir) ?
new DirectoryInfo(DevByLabelDir).GetFiles() : Enumerable.Empty<FileInfo>();
var labelDevPathPairs = labelDirEnum
.Select(x => (GetSymlinkTarget(x.FullName), x.Name));
var q1 = from mount in fProcMounts
join device in fProcPartitions on mount.Item1 equals device.Item2
join label in labelDevPathPairs on device.Item2 equals label.Item1 into labelMatches
from x in labelMatches.DefaultIfEmpty()
select new MountedVolumeInfo()
{
VolumePath = mount.Item2,
VolumeSizeBytes = device.Item1,
VolumeLabel = x.Name
};
var mountVolInfos = q1.ToArray();
if (_targetObs.SequenceEqual(mountVolInfos)) return;
else
{
var sub1 = await _udisk2Manager.WatchInterfacesAddedAsync(delegate { PollDbus(); });
var sub2 = await _udisk2Manager.WatchInterfacesRemovedAsync(delegate { PollDbus(); });
_targetObs.Clear();
_disposables.Add(sub1);
_disposables.Add(sub2);
PollDbus();
foreach (var i in mountVolInfos)
_targetObs.Add(i);
}
}
protected virtual void Dispose(bool disposing)
protected virtual void Dispose(bool disposing)
{
if (!_beenDisposed)
{
if (!disposedValue)
if (disposing)
{
if (disposing)
{
_disposables.Dispose();
_targetObs.Clear();
}
disposedValue = true;
_disposables.Dispose();
_targetObs.Clear();
}
}
public void Dispose()
{
Dispose(true);
_beenDisposed = true;
}
}
// https://stackoverflow.com/questions/19558644/update-an-observablecollection-from-another-collection
private void UpdateCollection(ObservableCollection<MountedVolumeInfo> target, IEnumerable<MountedVolumeInfo> newCollection)
{
var newCollectionEnumerator = newCollection.GetEnumerator();
var collectionEnumerator = target.GetEnumerator();
var itemsToDelete = new Collection<MountedVolumeInfo>();
while (collectionEnumerator.MoveNext())
{
var item = collectionEnumerator.Current;
if (!newCollection.Contains(item))
itemsToDelete.Add(item);
}
foreach (var itemToDelete in itemsToDelete)
{
target.Remove(itemToDelete);
}
var i = 0;
while (newCollectionEnumerator.MoveNext())
{
var item = newCollectionEnumerator.Current;
if (!target.Contains(item))
{
target.Insert(i, item);
}
else
{
int oldIndex = target.IndexOf(item);
target.Move(oldIndex, i);
}
i++;
}
}
public void Dispose()
{
Dispose(true);
}
}
}

2
src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoProvider.cs

@ -5,7 +5,7 @@ using Avalonia.Controls.Platform;
namespace Avalonia.FreeDesktop.Dbus
{
public partial class LinuxMountedVolumeInfoProvider : IMountedVolumeInfoProvider
public class LinuxMountedVolumeInfoProvider : IMountedVolumeInfoProvider
{
public IDisposable Listen(ObservableCollection<MountedVolumeInfo> mountedDrives)
{

1395
src/Avalonia.FreeDesktop/UDisks2.cs

File diff suppressed because it is too large
Loading…
Cancel
Save