You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The settings stores and BGLogger keep their files under Environment.SpecialFolder.CommonApplicationData. On Linux and macOS, .NET maps that folder to the fixed path /usr/share (dotnet/runtime Environment.GetFolderPathCore.Unix.cs:55, release/10.0; the line sits outside #if TARGET_OSX, so macOS gets it too). /usr/share is owned by root (drwxr-xr-x root root) and is meant for "architecture-independent resources of packages" (UAPI.9). Persistent system data belongs in /var/lib.
Affected code (main 02325dd; these files are unchanged since v3.0.8):
Application/ApplicationConfigurationStoreJSON.cs:110-114, ApplicationConfigurationStoreXML.cs:82-86, ApplicationConfigurationStoreBinary.cs:96-102: the getter for the default file name creates that folder, so the parameterless constructor already throws.
Logging/BGLoggerConfiguration.cs:33-37: the default LogDirectory is computed on its own from CommonApplicationData. It does not honourBAUERGROUP_ROAMINGAPPLICATIONDATA.
The same folder is also used by src/BAUERGROUP.Shared.Data/EmbeddedDatabase/ConcurrentPersistentDictionary.cs:80.
Observed on 2026-09-15 with a probe (Shared.Core 3.0.8, net10.0, self-contained linux-x64) on Ubuntu 26.04 under WSL2, running as a normal user (uid 1000):
'', because ~/.local/share did not exist yet. With the default option the runtime returns an empty string for a path that is not readable (GetFolderPathCore.Unix.cs:28-40).
Running as root would hide the problem, but the application would then write its settings and logs into /usr/share.
Proposal
Use one resolver for the settings stores, the log directory and the Sentry cache (#133):
ApplicationFolders.ExecutionAutomaticApplicationDataFolder is the only rule:
Windows: unchanged (%ProgramData%\<App>, or %APPDATA%\<App> with the variable).
BAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE, any OS: unchanged (SpecialFolder.ApplicationData, which is $XDG_CONFIG_HOME or ~/.config on Linux). Nothing moves for anyone who already uses the variable.
Otherwise, when not on Windows:
Use /var/lib/<App> if that directory exists. The installer or package creates it with the ownership the station needs, e.g. install -d -o <user> /var/lib/<App>, or systemd StateDirectory=<App>.
Otherwise use the per-user folder Environment.GetFolderPath(SpecialFolder.LocalApplicationData, SpecialFolderOption.DoNotVerify) plus <App>. That is $XDG_DATA_HOME or ~/.local/share on Linux (runtime :124-131), and ~/Library/Application Support on macOS (:101-103). DoNotVerify avoids the empty string shown above. The overload is available in netstandard2.0.
Log directory: the default LogDirectory becomes Path.Combine(ExecutionAutomaticApplicationDataFolder, "Logging") instead of its own CommonApplicationData lookup.
Directory creation: resolving a path should not create the folder. Create it on save, as ApplicationConfigurationStoreJSON.Save already does at :85-89. A location that cannot be written then fails when writing, not in the constructor.
Tests:
the resolver, with the OS, environment variables, home folder and the existence check injected
the file target writing into a real subdirectory on Linux CI
Trade-offs:
Existence check for /var/lib/<App>, no write test. It is deterministic and cheap. A folder created with the wrong owner fails visibly when saving, instead of silently falling back to a per-user folder. A silent fallback would split one station's settings by login user.
Per-user fallback. Two OS users on one station get separate settings, as on Windows with the variable today.
Behaviour change on Windows. Point 2 changes two things on Windows:
With BAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE, the log follows the variable to %APPDATA%\<App>\Logging.
The folder is named after the executable (ApplicationFolders), not after BGLoggerConfiguration.ApplicationName. An application that sets a custom ApplicationName would find its log in a different folder.
Both should be in the release notes. If that is unwanted, apply point 2 on non-Windows only.
bgIndustrialAutomation Client (bauer-group/OT-AutomationClient) is preparing Linux stations.
Its settings store gets its path from ApplicationFolders.ExecutionAutomaticApplicationDataFolder (src/BAUERGROUP.bgIndustrialAutomation.Client.Core/Configuration/ApplicationSettingsStore.cs:80-83, origin/main 3f6fb39).
It derives its Sentry cache folder from the same path (src/BAUERGROUP.bgIndustrialAutomation.Client.App/ErrorReporting.cs:362-364).
On Linux, saving settings throws UnauthorizedAccessException, so a station always restarts on defaults, and no log file is written.
The only workaround without code changes is BAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE. It moves the settings and the cache to ~/.config/<App>/, but not the log. The application's documentation lists Linux as untested and says the settings, log and cache would need a location other than /usr/share (docs/ERROR-REPORTING.md:1166-1168).
Problem
The settings stores and
BGLoggerkeep their files underEnvironment.SpecialFolder.CommonApplicationData. On Linux and macOS, .NET maps that folder to the fixed path/usr/share(dotnet/runtimeEnvironment.GetFolderPathCore.Unix.cs:55, release/10.0; the line sits outside#if TARGET_OSX, so macOS gets it too)./usr/shareis owned by root (drwxr-xr-x root root) and is meant for "architecture-independent resources of packages" (UAPI.9). Persistent system data belongs in/var/lib.Affected code (main
02325dd; these files are unchanged sincev3.0.8):src/BAUERGROUP.Shared.Core/Application/ApplicationFolders.cs:62,:73,:131-141:ExecutionAutomaticApplicationDataFolderreturns/usr/share/<App>unlessBAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE.Application/ApplicationConfigurationStoreJSON.cs:110-114,ApplicationConfigurationStoreXML.cs:82-86,ApplicationConfigurationStoreBinary.cs:96-102: the getter for the default file name creates that folder, so the parameterless constructor already throws.Logging/BGLoggerConfiguration.cs:33-37: the defaultLogDirectoryis computed on its own fromCommonApplicationData. It does not honourBAUERGROUP_ROAMINGAPPLICATIONDATA.Logging/BGLoggerConfiguration.cs:547,:550:FileNameandArchiveFileNamejoin the parts with\, so on Linux even a writableLogDirectorygets no log inside it. That is a separate cause with its own fix, tracked in BGLogger file target uses a hard-coded backslash, so on Linux/macOS the log is written as a file named "Logging\<App>.log" #134.src/BAUERGROUP.Shared.Data/EmbeddedDatabase/ConcurrentPersistentDictionary.cs:80.Observed on 2026-09-15 with a probe (Shared.Core 3.0.8, net10.0, self-contained linux-x64) on Ubuntu 26.04 under WSL2, running as a normal user (uid 1000):
ApplicationFolders.ExecutionAutomaticApplicationDataFolder/usr/share/DirsProbenew ApplicationConfigurationStoreJSON<T>()System.UnauthorizedAccessException: Access to the path '/usr/share/DirsProbe' is denied.BGLogger.Info(...)with the defaultLogDirectory(/usr/share/DirsProbe/Logging)BAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE~/.config/DirsProbe/DirsProbe.Configuration.json, butLogDirectoryis still/usr/share/DirsProbe/LoggingLogDirectory = "/tmp/dirsprobe/Logging"(writable)Loggingdirectory;/tmp/dirsprobecontains one file literally namedLogging\DirsProbe.log(#134)Environment.GetFolderPath(SpecialFolder.LocalApplicationData)'', because~/.local/sharedid not exist yet. With the default option the runtime returns an empty string for a path that is not readable (GetFolderPathCore.Unix.cs:28-40).Running as root would hide the problem, but the application would then write its settings and logs into
/usr/share.Proposal
Use one resolver for the settings stores, the log directory and the Sentry cache (#133):
ApplicationFolders.ExecutionAutomaticApplicationDataFolderis the only rule:%ProgramData%\<App>, or%APPDATA%\<App>with the variable).BAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE, any OS: unchanged (SpecialFolder.ApplicationData, which is$XDG_CONFIG_HOMEor~/.configon Linux). Nothing moves for anyone who already uses the variable./var/lib/<App>if that directory exists. The installer or package creates it with the ownership the station needs, e.g.install -d -o <user> /var/lib/<App>, or systemdStateDirectory=<App>.Environment.GetFolderPath(SpecialFolder.LocalApplicationData, SpecialFolderOption.DoNotVerify)plus<App>. That is$XDG_DATA_HOMEor~/.local/shareon Linux (runtime:124-131), and~/Library/Application Supporton macOS (:101-103).DoNotVerifyavoids the empty string shown above. The overload is available in netstandard2.0.LogDirectorybecomesPath.Combine(ExecutionAutomaticApplicationDataFolder, "Logging")instead of its ownCommonApplicationDatalookup.ApplicationConfigurationStoreJSON.Savealready does at:85-89. A location that cannot be written then fails when writing, not in the constructor.Trade-offs:
Existence check for
/var/lib/<App>, no write test. It is deterministic and cheap. A folder created with the wrong owner fails visibly when saving, instead of silently falling back to a per-user folder. A silent fallback would split one station's settings by login user.Per-user fallback. Two OS users on one station get separate settings, as on Windows with the variable today.
Behaviour change on Windows. Point 2 changes two things on Windows:
BAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE, the log follows the variable to%APPDATA%\<App>\Logging.ApplicationFolders), not afterBGLoggerConfiguration.ApplicationName. An application that sets a customApplicationNamewould find its log in a different folder.Both should be in the release notes. If that is unwanted, apply point 2 on non-Windows only.
Logs stay in the data folder. They do not go to
/var/logor$XDG_STATE_HOME. This keeps one folder per application with the same layout as on Windows, and Enable the Sentry offline cache by default, keeping 50 reports #133's cache fits in it.macOS follows the same code path but has not been tested.
Related
\in the log file target. Independent of the folder rule and a two-line fix.<App>part of these paths comes fromAssembly.Location, which is empty in single-file builds.BGLoggerConfiguration.cs:24-30also derives the name separately fromApplicationFolders. Fixing both in one release gives one name and one folder, and installed stations move only once./usr/share, where it cannot be created, and error reporting then stays off without any notice (see the last bullet of Enable the Sentry offline cache by default, keeping 50 reports #133).Context
bgIndustrialAutomation Client (bauer-group/OT-AutomationClient) is preparing Linux stations.
ApplicationFolders.ExecutionAutomaticApplicationDataFolder(src/BAUERGROUP.bgIndustrialAutomation.Client.Core/Configuration/ApplicationSettingsStore.cs:80-83, origin/main3f6fb39).src/BAUERGROUP.bgIndustrialAutomation.Client.App/ErrorReporting.cs:362-364).On Linux, saving settings throws
UnauthorizedAccessException, so a station always restarts on defaults, and no log file is written.The only workaround without code changes is
BAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE. It moves the settings and the cache to~/.config/<App>/, but not the log. The application's documentation lists Linux as untested and says the settings, log and cache would need a location other than/usr/share(docs/ERROR-REPORTING.md:1166-1168).