1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . IO ;
4
+ using System . Text ;
5
+
6
+ namespace GitCredentialManager . Interop . Posix ;
7
+
8
+ public class LinuxSettings : Settings
9
+ {
10
+ private readonly ITrace _trace ;
11
+ private readonly IFileSystem _fs ;
12
+
13
+ private IDictionary < string , string > _extConfigCache ;
14
+
15
+ /// <summary>
16
+ /// Reads settings from Git configuration, environment variables, and defaults from the
17
+ /// /etc/git-credential-manager.d app configuration directory.
18
+ /// </summary>
19
+ public LinuxSettings ( IEnvironment environment , IGit git , ITrace trace , IFileSystem fs )
20
+ : base ( environment , git )
21
+ {
22
+ EnsureArgument . NotNull ( trace , nameof ( trace ) ) ;
23
+ EnsureArgument . NotNull ( fs , nameof ( fs ) ) ;
24
+
25
+ _trace = trace ;
26
+ _fs = fs ;
27
+
28
+ PlatformUtils . EnsurePosix ( ) ;
29
+ }
30
+
31
+ protected override bool TryGetExternalDefault ( string section , string scope , string property , out string value )
32
+ {
33
+ value = null ;
34
+
35
+ _extConfigCache ??= ReadExternalConfiguration ( ) ;
36
+
37
+ string name = string . IsNullOrWhiteSpace ( scope )
38
+ ? $ "{ section } .{ property } "
39
+ : $ "{ section } .{ scope } .{ property } ";
40
+
41
+ // Check if the setting exists in the configuration
42
+ if ( ! _extConfigCache ? . TryGetValue ( name , out value ) ?? false )
43
+ {
44
+ // No property exists (or failed to read config)
45
+ return false ;
46
+ }
47
+
48
+ _trace . WriteLine ( $ "Default setting found in app configuration directory: { name } ={ value } ") ;
49
+ return true ;
50
+ }
51
+
52
+ private Dictionary < string , string > ReadExternalConfiguration ( )
53
+ {
54
+ try
55
+ {
56
+ // Check for system-wide config files in /etc/git-credential-manager/config.d and concatenate them together
57
+ // in alphabetical order to form a single configuration.
58
+ const string configDir = Constants . LinuxAppDefaultsDirectoryPath ;
59
+ if ( ! _fs . DirectoryExists ( configDir ) )
60
+ {
61
+ // No configuration directory exists
62
+ return null ;
63
+ }
64
+
65
+ // Get all the files in the configuration directory
66
+ IEnumerable < string > files = _fs . EnumerateFiles ( configDir , "*" ) ;
67
+
68
+ // Read the contents of each file and concatenate them together
69
+ var combinedFile = new StringBuilder ( ) ;
70
+ foreach ( string file in files )
71
+ {
72
+ using Stream stream = _fs . OpenFileStream ( file , FileMode . Open , FileAccess . Read , FileShare . Read ) ;
73
+ using var reader = new StreamReader ( stream ) ;
74
+ string contents = reader . ReadToEnd ( ) ;
75
+ combinedFile . Append ( contents ) ;
76
+ combinedFile . Append ( '\n ' ) ;
77
+ }
78
+
79
+ // Parse the configuration in to a dictionary
80
+ // Each line is a setting in the format: name=value\n
81
+ var config = new Dictionary < string , string > ( GitConfigurationKeyComparer . Instance ) ;
82
+ string [ ] lines = combinedFile . ToString ( ) . Split ( '\n ' , StringSplitOptions . RemoveEmptyEntries ) ;
83
+ foreach ( string line in lines )
84
+ {
85
+ if ( string . IsNullOrWhiteSpace ( line ) )
86
+ {
87
+ // Skip empty lines
88
+ continue ;
89
+ }
90
+
91
+ if ( line . TrimStart ( ) . StartsWith ( '#' ) )
92
+ {
93
+ // Skip comments
94
+ continue ;
95
+ }
96
+
97
+ string [ ] parts = line . Split ( '=' , count : 2 ) ;
98
+ if ( parts . Length != 2 )
99
+ {
100
+ // Invalid line format
101
+ continue ;
102
+ }
103
+
104
+ string k = parts [ 0 ] ;
105
+ string v = parts [ 1 ] ;
106
+ config [ k ] = v ;
107
+ }
108
+
109
+ return config ;
110
+ }
111
+ catch ( Exception ex )
112
+ {
113
+ // Reading defaults is not critical to the operation of the application
114
+ // so we can ignore any errors and just log the failure.
115
+ _trace . WriteLine ( "Failed to read default setting from app configuration directory." ) ;
116
+ _trace . WriteException ( ex ) ;
117
+ return null ;
118
+ }
119
+ }
120
+ }
0 commit comments