Compare commits

...

2 Commits

Author SHA1 Message Date
KeatonTheBot
81eddcadc5 Merge branch 'fix/linux-file-picker' into 'master'
Linux: Fix file picker not launching from disabling core dumps

See merge request [ryubing/ryujinx!249](https://git.ryujinx.app/ryubing/ryujinx/-/merge_requests/249)
2026-02-19 11:02:29 -06:00
KeatonTheBot
224142b9c5 Linux: Fix file picker not launching from disabling core dumps
Core dumps are disabled by default on Linux, but this prevents access to the file picker due to security hardening. To work around this, core dumps are selectively enabled and disabled around the file picker tasks.
2026-02-19 11:02:24 -06:00
9 changed files with 124 additions and 2 deletions

View File

@@ -22,10 +22,11 @@ namespace Ryujinx.Common.Utilities
} }
// "dumpable" attribute of the calling process // "dumpable" attribute of the calling process
private const int PR_GET_DUMPABLE = 3;
private const int PR_SET_DUMPABLE = 4; private const int PR_SET_DUMPABLE = 4;
[DllImport("libc", SetLastError = true)] [LibraryImport("libc", SetLastError = true)]
private static extern int prctl(int option, int arg2); private static partial int prctl(int option, int arg2);
public static void SetCoreDumpable(bool dumpable) public static void SetCoreDumpable(bool dumpable)
{ {
@@ -36,5 +37,13 @@ namespace Ryujinx.Common.Utilities
Debug.Assert(result == 0); Debug.Assert(result == 0);
} }
} }
// Use the below line to display dumpable status in the console:
// Console.WriteLine($"{OsUtils.IsCoreDumpable()}");
public static bool IsCoreDumpable()
{
int result = prctl(PR_GET_DUMPABLE, 0);
return result == 1;
}
} }
} }

View File

@@ -18,6 +18,7 @@ using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
@@ -410,6 +411,8 @@ namespace Ryujinx.Ava.Common
public static async Task ExtractAoc(IStorageProvider storageProvider, string updateFilePath, string updateName) public static async Task ExtractAoc(IStorageProvider storageProvider, string updateFilePath, string updateName)
{ {
OsUtils.SetCoreDumpable(true);
Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
@@ -419,10 +422,17 @@ namespace Ryujinx.Ava.Common
return; return;
ExtractAoc(result.Value.Path.LocalPath, updateFilePath, updateName); ExtractAoc(result.Value.Path.LocalPath, updateFilePath, updateName);
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0) public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
{ {
OsUtils.SetCoreDumpable(true);
Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
@@ -432,6 +442,11 @@ namespace Ryujinx.Ava.Common
return; return;
ExtractSection(result.Value.Path.LocalPath, ncaSectionType, titleFilePath, titleName, programIndex); ExtractSection(result.Value.Path.LocalPath, ncaSectionType, titleFilePath, titleName, programIndex);
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath, CancellationToken token) public static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath, CancellationToken token)

View File

@@ -42,6 +42,7 @@ namespace Ryujinx.Ava
public static bool PreviewerDetached { get; private set; } public static bool PreviewerDetached { get; private set; }
public static bool UseHardwareAcceleration { get; private set; } public static bool UseHardwareAcceleration { get; private set; }
public static string BackendThreadingArg { get; private set; } public static string BackendThreadingArg { get; private set; }
public static bool CoreDumpArg { get; private set; }
private const uint MbIconwarning = 0x30; private const uint MbIconwarning = 0x30;
@@ -81,6 +82,8 @@ namespace Ryujinx.Ava
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui"); bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps"); bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps");
CoreDumpArg = coreDumpArg;
// TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception. // TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception.
// This is undesirable and causes very odd behavior during development (the process stops responding, // This is undesirable and causes very odd behavior during development (the process stops responding,
// the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user. // the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user.

View File

@@ -8,6 +8,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
@@ -128,6 +129,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void Add() public async void Add()
{ {
OsUtils.SetCoreDumpable(true);
IReadOnlyList<IStorageFile> result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions IReadOnlyList<IStorageFile> result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
@@ -158,6 +161,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
await ShowNewDlcAddedDialog(totalDlcAdded); await ShowNewDlcAddedDialog(totalDlcAdded);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
private bool AddDownloadableContent(string path, out int numDlcAdded) private bool AddDownloadableContent(string path, out int numDlcAdded)

View File

@@ -1266,6 +1266,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey, private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey,
LoadContentFromFolderDelegate onDirsSelected, LocaleKeys dirSelectDialogTitle) LoadContentFromFolderDelegate onDirsSelected, LocaleKeys dirSelectDialogTitle)
{ {
OsUtils.SetCoreDumpable(true);
Optional<IReadOnlyList<IStorageFolder>> result = Optional<IReadOnlyList<IStorageFolder>> result =
await StorageProvider.OpenMultiFolderPickerAsync( await StorageProvider.OpenMultiFolderPickerAsync(
new FolderPickerOpenOptions { Title = LocaleManager.Instance[dirSelectDialogTitle] }); new FolderPickerOpenOptions { Title = LocaleManager.Instance[dirSelectDialogTitle] });
@@ -1293,6 +1295,11 @@ namespace Ryujinx.Ava.UI.ViewModels
(int)Symbol.Checkmark); (int)Symbol.Checkmark);
}); });
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
#endregion #endregion
@@ -1355,6 +1362,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task InstallFirmwareFromFile() public async Task InstallFirmwareFromFile()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
{ {
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>
@@ -1384,20 +1393,34 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
await HandleFirmwareInstallation(result.Value.Path.LocalPath); await HandleFirmwareInstallation(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public async Task InstallFirmwareFromFolder() public async Task InstallFirmwareFromFolder()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(); Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync();
if (result.HasValue) if (result.HasValue)
{ {
await HandleFirmwareInstallation(result.Value.Path.LocalPath); await HandleFirmwareInstallation(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public async Task InstallKeysFromFile() public async Task InstallKeysFromFile()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
{ {
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>
@@ -1415,16 +1438,28 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
await HandleKeysInstallation(result.Value.Path.LocalPath); await HandleKeysInstallation(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public async Task InstallKeysFromFolder() public async Task InstallKeysFromFolder()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(); Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync();
if (result.HasValue) if (result.HasValue)
{ {
await HandleKeysInstallation(result.Value.Path.LocalPath); await HandleKeysInstallation(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public void OpenRyujinxFolder() public void OpenRyujinxFolder()
@@ -1528,6 +1563,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task OpenFile() public async Task OpenFile()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.LoadApplicationFromFileDialogTitle], Title = LocaleManager.Instance[LocaleKeys.LoadApplicationFromFileDialogTitle],
@@ -1599,6 +1636,11 @@ namespace Ryujinx.Ava.UI.ViewModels
LocaleManager.Instance[LocaleKeys.MenuBarFileOpenFromFileError]); LocaleManager.Instance[LocaleKeys.MenuBarFileOpenFromFileError]);
} }
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public async Task LoadDlcFromFolder() public async Task LoadDlcFromFolder()
@@ -1621,6 +1663,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task OpenFolder() public async Task OpenFolder()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync( Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(
new FolderPickerOpenOptions new FolderPickerOpenOptions
{ {
@@ -1636,6 +1680,11 @@ namespace Ryujinx.Ava.UI.ViewModels
await LoadApplication(applicationData); await LoadApplication(applicationData);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public static bool InitializeUserConfig(ApplicationData application) public static bool InitializeUserConfig(ApplicationData application)
@@ -1843,6 +1892,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task OpenBinFile() public async Task OpenBinFile()
{ {
OsUtils.SetCoreDumpable(true);
if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning) if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning)
{ {
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync( Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(
@@ -1862,6 +1913,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
AppHost.Device.System.ScanAmiiboFromBin(result.Value.Path.LocalPath); AppHost.Device.System.ScanAmiiboFromBin(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
} }

View File

@@ -288,6 +288,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void Add() public async void Add()
{ {
OsUtils.SetCoreDumpable(true);
IReadOnlyList<IStorageFolder> result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions IReadOnlyList<IStorageFolder> result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.SelectModDialogTitle], Title = LocaleManager.Instance[LocaleKeys.SelectModDialogTitle],
@@ -298,6 +300,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
AddMod(new DirectoryInfo(folder.Path.LocalPath)); AddMod(new DirectoryInfo(folder.Path.LocalPath));
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public void DeleteAll() public void DeleteAll()

View File

@@ -7,6 +7,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -148,6 +149,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task Add() public async Task Add()
{ {
OsUtils.SetCoreDumpable(true);
IReadOnlyList<IStorageFile> result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions IReadOnlyList<IStorageFile> result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
AllowMultiple = true, AllowMultiple = true,
@@ -177,6 +180,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
await ShowNewUpdatesAddedDialog(totalUpdatesAdded); await ShowNewUpdatesAddedDialog(totalUpdatesAdded);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public void Save() public void Save()

View File

@@ -7,6 +7,7 @@ using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Common.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -27,6 +28,8 @@ namespace Ryujinx.Ava.UI.Views.Settings
private async Task AddDirButton(TextBox addDirBox, AvaloniaList<string> directories) private async Task AddDirButton(TextBox addDirBox, AvaloniaList<string> directories)
{ {
OsUtils.SetCoreDumpable(true);
string path = addDirBox.Text; string path = addDirBox.Text;
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !directories.Contains(path)) if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !directories.Contains(path))
@@ -48,6 +51,11 @@ namespace Ryujinx.Ava.UI.Views.Settings
ViewModel.GameListNeedsRefresh = true; ViewModel.GameListNeedsRefresh = true;
} }
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
private void RemoveGameDirButton_OnClick(object sender, RoutedEventArgs e) private void RemoveGameDirButton_OnClick(object sender, RoutedEventArgs e)

View File

@@ -8,6 +8,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using SkiaSharp; using SkiaSharp;
using System.Collections.Generic; using System.Collections.Generic;
@@ -62,6 +63,8 @@ namespace Ryujinx.Ava.UI.Views.User
private async void Import_OnClick(object sender, RoutedEventArgs e) private async void Import_OnClick(object sender, RoutedEventArgs e)
{ {
OsUtils.SetCoreDumpable(true);
IReadOnlyList<IStorageFile> result = await ((Window)this.GetVisualRoot()!).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions IReadOnlyList<IStorageFile> result = await ((Window)this.GetVisualRoot()!).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
AllowMultiple = false, AllowMultiple = false,
@@ -81,6 +84,11 @@ namespace Ryujinx.Ava.UI.Views.User
_profile.Image = ProcessProfileImage(File.ReadAllBytes(result[0].Path.LocalPath)); _profile.Image = ProcessProfileImage(File.ReadAllBytes(result[0].Path.LocalPath));
_parent.GoBack(); _parent.GoBack();
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
private void GoBack(object sender, RoutedEventArgs e) private void GoBack(object sender, RoutedEventArgs e)