1 //------------------------------------------------------------------------------
3 // System.Environment.cs
5 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
7 // Author: Jim Richardson, develop@wtfo-guru.com
8 // Dan Lewis (dihlewis@yahoo.co.uk)
9 // Created: Saturday, August 11, 2001
11 //------------------------------------------------------------------------------
13 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
37 using System.Runtime.CompilerServices;
38 using System.Security;
39 using System.Security.Permissions;
41 using System.Runtime.InteropServices;
46 public static class Environment {
49 * This is the version number of the corlib-runtime interface. When
50 * making changes to this interface (by changing the layout
51 * of classes the runtime knows about, changing icall signature or
52 * semantics etc), increment this variable. Also increment the
53 * pair of this variable in the runtime in metadata/appdomain.c.
54 * Changes which are already detected at runtime, like the addition
55 * of icalls, do not require an increment.
57 private const int mono_corlib_version = 87;
60 public enum SpecialFolder
73 DesktopDirectory = 0x10,
75 ApplicationData = 0x1a,
76 LocalApplicationData = 0x1c,
80 CommonApplicationData = 0x23,
84 CommonProgramFiles = 0x2b,
88 /// Gets the command line for this process
90 public static string CommandLine {
91 // note: security demand inherited from calling GetCommandLineArgs
93 // FIXME: we may need to quote, but any sane person
94 // should use GetCommandLineArgs () instead.
95 return String.Join (" ", GetCommandLineArgs ());
100 /// Gets or sets the current directory. Actually this is supposed to get
101 /// and/or set the process start directory acording to the documentation
102 /// but actually test revealed at beta2 it is just Getting/Setting the CurrentDirectory
104 public static string CurrentDirectory
107 return Directory.GetCurrentDirectory ();
110 Directory.SetCurrentDirectory (value);
115 /// Gets or sets the exit code of this process
117 public extern static int ExitCode
119 [MethodImplAttribute (MethodImplOptions.InternalCall)]
121 [MethodImplAttribute (MethodImplOptions.InternalCall)]
125 static public extern bool HasShutdownStarted
127 [MethodImplAttribute (MethodImplOptions.InternalCall)]
133 /// Gets the name of the local computer
135 public extern static string MachineName {
136 [MethodImplAttribute (MethodImplOptions.InternalCall)]
137 [EnvironmentPermission (SecurityAction.Demand, Read="COMPUTERNAME")]
138 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
143 /// Gets the standard new line value
145 public extern static string NewLine {
146 [MethodImplAttribute (MethodImplOptions.InternalCall)]
151 // Support methods and fields for OSVersion property
153 static OperatingSystem os;
155 internal static extern PlatformID Platform {
156 [MethodImplAttribute (MethodImplOptions.InternalCall)]
160 [MethodImplAttribute (MethodImplOptions.InternalCall)]
161 internal static extern string GetOSVersionString ();
164 /// Gets the current OS version information
166 public static OperatingSystem OSVersion {
169 Version v = Version.CreateFromString (GetOSVersionString ());
170 PlatformID p = Platform;
171 os = new OperatingSystem (p, v);
180 public static string StackTrace {
181 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
183 System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace (0, true);
184 return trace.ToString ();
189 /// Get a fully qualified path to the system directory
191 public static string SystemDirectory {
193 return GetFolderPath (SpecialFolder.System);
198 /// Get the number of milliseconds that have elapsed since the system was booted
200 public extern static int TickCount {
201 [MethodImplAttribute (MethodImplOptions.InternalCall)]
206 /// Get UserDomainName
208 public static string UserDomainName {
209 // FIXME: this variable doesn't exist (at least not on WinXP) - reported to MS as FDBK20562
210 [EnvironmentPermission (SecurityAction.Demand, Read="USERDOMAINNAME")]
217 /// Gets a flag indicating whether the process is in interactive mode
219 [MonoTODO ("Currently always returns false, regardless of interactive state")]
220 public static bool UserInteractive {
227 /// Get the user name of current process is running under
229 public extern static string UserName {
230 [MethodImplAttribute (MethodImplOptions.InternalCall)]
231 [EnvironmentPermission (SecurityAction.Demand, Read="USERNAME;USER")]
236 /// Get the version of the common language runtime
238 public static Version Version {
240 return new Version (Consts.FxFileVersion);
245 /// Get the amount of physical memory mapped to process
247 [MonoTODO ("Currently always returns zero")]
248 public static long WorkingSet {
249 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
253 [MethodImplAttribute (MethodImplOptions.InternalCall)]
254 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
255 public extern static void Exit (int exitCode);
258 /// Substitute environment variables in the argument "name"
260 public static string ExpandEnvironmentVariables (string name)
263 throw new ArgumentNullException ("name");
265 int off1 = name.IndexOf ('%');
269 int len = name.Length;
271 if (off1 == len - 1 || (off2 = name.IndexOf ('%', off1 + 1)) == -1)
274 StringBuilder result = new StringBuilder ();
275 result.Append (name, 0, off1);
276 Hashtable tbl = null;
278 string var = name.Substring (off1 + 1, off2 - off1 - 1);
279 string value = GetEnvironmentVariable (var);
280 if (value == null && Environment.IsRunningOnWindows) {
281 // On windows, env. vars. are case insensitive
283 tbl = GetEnvironmentVariablesNoCase ();
285 value = tbl [var] as string;
288 // If value not found, add %FOO to stream,
289 // and use the closing % for the next iteration.
290 // If value found, expand it in place of %FOO%
296 result.Append (value);
299 off1 = name.IndexOf ('%', off2 + 1);
300 // If no % found for off1, don't look for one for off2
301 off2 = (off1 == -1 || off2 > len-1)? -1 :name.IndexOf ('%', off1 + 1);
302 // textLen is the length of text between the closing % of current iteration
303 // and the starting % of the next iteration if any. This text is added to output
305 // If no new % found, use all the remaining text
306 if (off1 == -1 || off2 == -1)
307 textLen = len - oldOff2 - 1;
308 // If value found in current iteration, use text after current closing % and next %
309 else if(value != null)
310 textLen = off1 - oldOff2 - 1;
311 // If value not found in current iteration, but a % was found for next iteration,
312 // use text from current closing % to the next %.
314 textLen = off1 - oldOff2;
315 if(off1 >= oldOff2 || off1 == -1)
316 result.Append (name, oldOff2+1, textLen);
317 } while (off2 > -1 && off2 < len);
319 return result.ToString ();
324 /// Return an array of the command line arguments of the current process
326 [MethodImplAttribute (MethodImplOptions.InternalCall)]
327 [EnvironmentPermissionAttribute (SecurityAction.Demand, Read = "PATH")]
328 public extern static string[] GetCommandLineArgs ();
330 [MethodImplAttribute (MethodImplOptions.InternalCall)]
331 internal extern static string internalGetEnvironmentVariable (string variable);
334 /// Return a string containing the value of the environment
335 /// variable identifed by parameter "variable"
337 public static string GetEnvironmentVariable (string variable)
340 if (SecurityManager.SecurityEnabled) {
341 new EnvironmentPermission (EnvironmentPermissionAccess.Read, variable).Demand ();
344 return internalGetEnvironmentVariable (variable);
347 static Hashtable GetEnvironmentVariablesNoCase ()
349 Hashtable vars = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
350 CaseInsensitiveComparer.Default);
352 foreach (string name in GetEnvironmentVariableNames ()) {
353 vars [name] = internalGetEnvironmentVariable (name);
360 /// Return a set of all environment variables and their values
363 public static IDictionary GetEnvironmentVariables ()
365 StringBuilder sb = null;
366 if (SecurityManager.SecurityEnabled) {
367 // we must have access to each variable to get the lot
368 sb = new StringBuilder ();
369 // but (performance-wise) we do not want a stack-walk
370 // for each of them so we concatenate them
373 Hashtable vars = new Hashtable ();
374 foreach (string name in GetEnvironmentVariableNames ()) {
375 vars [name] = internalGetEnvironmentVariable (name);
383 new EnvironmentPermission (EnvironmentPermissionAccess.Read, sb.ToString ()).Demand ();
388 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
389 public static IDictionary GetEnvironmentVariables ()
391 Hashtable vars = new Hashtable ();
392 foreach (string name in GetEnvironmentVariableNames ()) {
393 vars [name] = internalGetEnvironmentVariable (name);
399 [MethodImplAttribute (MethodImplOptions.InternalCall)]
400 private extern static string GetWindowsFolderPath (int folder);
403 /// Returns the fully qualified path of the
404 /// folder specified by the "folder" parameter
406 public static string GetFolderPath (SpecialFolder folder)
410 if (Environment.IsRunningOnWindows) {
411 dir = GetWindowsFolderPath ((int) folder);
413 dir = InternalGetFolderPath (folder);
416 if ((dir != null) && (dir.Length > 0) && SecurityManager.SecurityEnabled) {
417 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dir).Demand ();
423 private static string ReadXdgUserDir (string config_dir, string home_dir,
424 string key, string fallback)
426 string env_path = internalGetEnvironmentVariable (key);
427 if (env_path != null && env_path != String.Empty) {
431 string user_dirs_path = Path.Combine (config_dir, "user-dirs.dirs");
433 if (!File.Exists (user_dirs_path)) {
434 return Path.Combine (home_dir, fallback);
438 using(StreamReader reader = new StreamReader (user_dirs_path)) {
440 while ((line = reader.ReadLine ()) != null) {
442 int delim_index = line.IndexOf ('=');
443 if(delim_index > 8 && line.Substring (0, delim_index) == key) {
444 string path = line.Substring (delim_index + 1).Trim ('"');
445 bool relative = false;
447 if (path.StartsWith ("$HOME/")) {
449 path = path.Substring (6);
450 } else if (!path.StartsWith ("/")) {
454 return relative ? Path.Combine (home_dir, path) : path;
458 } catch (FileNotFoundException) {
461 return Path.Combine (home_dir, fallback);
465 // the security runtime (and maybe other parts of corlib) needs the
466 // information to initialize themselves before permissions can be checked
467 internal static string InternalGetFolderPath (SpecialFolder folder)
469 string home = internalGetHome ();
471 // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
473 // note: skip security check for environment variables
474 string data = internalGetEnvironmentVariable ("XDG_DATA_HOME");
475 if ((data == null) || (data == String.Empty)) {
476 data = Path.Combine (home, ".local");
477 data = Path.Combine (data, "share");
480 // note: skip security check for environment variables
481 string config = internalGetEnvironmentVariable ("XDG_CONFIG_HOME");
482 if ((config == null) || (config == String.Empty)) {
483 config = Path.Combine (home, ".config");
487 // MyComputer is a virtual directory
488 case SpecialFolder.MyComputer:
492 case SpecialFolder.Personal:
494 return Path.Combine (home, "Documents");
498 // use FDO's CONFIG_HOME. This data will be synced across a network like the windows counterpart.
499 case SpecialFolder.ApplicationData:
501 //use FDO's DATA_HOME. This is *NOT* synced
502 case SpecialFolder.LocalApplicationData:
504 case SpecialFolder.Desktop:
505 case SpecialFolder.DesktopDirectory:
506 return ReadXdgUserDir (config, home, "XDG_DESKTOP_DIR", "Desktop");
508 case SpecialFolder.MyMusic:
509 return ReadXdgUserDir (config, home, "XDG_MUSIC_DIR", "Music");
511 case SpecialFolder.MyPictures:
512 return ReadXdgUserDir (config, home, "XDG_PICTURES_DIR", "Pictures");
514 // these simply dont exist on Linux
515 // The spec says if a folder doesnt exist, we
517 case SpecialFolder.Favorites:
518 case SpecialFolder.Programs:
519 case SpecialFolder.SendTo:
520 case SpecialFolder.StartMenu:
521 case SpecialFolder.Startup:
522 case SpecialFolder.Templates:
523 case SpecialFolder.Cookies:
524 case SpecialFolder.History:
525 case SpecialFolder.InternetCache:
526 case SpecialFolder.Recent:
527 case SpecialFolder.CommonProgramFiles:
528 case SpecialFolder.ProgramFiles:
529 case SpecialFolder.System:
531 // This is where data common to all users goes
532 case SpecialFolder.CommonApplicationData:
535 throw new ArgumentException ("Invalid SpecialFolder");
539 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
540 public static string[] GetLogicalDrives ()
542 return GetLogicalDrivesInternal ();
546 [MethodImplAttribute (MethodImplOptions.InternalCall)]
547 private static extern void internalBroadcastSettingChange ();
549 public static string GetEnvironmentVariable (string variable, EnvironmentVariableTarget target)
552 case EnvironmentVariableTarget.Process:
553 return GetEnvironmentVariable (variable);
554 case EnvironmentVariableTarget.Machine:
555 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
556 if (!IsRunningOnWindows)
558 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
559 object regvalue = env.GetValue (variable);
560 return (regvalue == null) ? null : regvalue.ToString ();
562 case EnvironmentVariableTarget.User:
563 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
564 if (!IsRunningOnWindows)
566 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", false)) {
567 object regvalue = env.GetValue (variable);
568 return (regvalue == null) ? null : regvalue.ToString ();
571 throw new ArgumentException ("target");
575 public static IDictionary GetEnvironmentVariables (EnvironmentVariableTarget target)
577 IDictionary variables = (IDictionary)new Hashtable ();
579 case EnvironmentVariableTarget.Process:
580 variables = GetEnvironmentVariables ();
582 case EnvironmentVariableTarget.Machine:
583 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
584 if (IsRunningOnWindows) {
585 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
586 string[] value_names = env.GetValueNames ();
587 foreach (string value_name in value_names)
588 variables.Add (value_name, env.GetValue (value_name));
592 case EnvironmentVariableTarget.User:
593 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
594 if (IsRunningOnWindows) {
595 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment")) {
596 string[] value_names = env.GetValueNames ();
597 foreach (string value_name in value_names)
598 variables.Add (value_name, env.GetValue (value_name));
603 throw new ArgumentException ("target");
608 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
609 public static void SetEnvironmentVariable (string variable, string value)
611 SetEnvironmentVariable (variable, value, EnvironmentVariableTarget.Process);
614 [EnvironmentPermission (SecurityAction.Demand, Unrestricted = true)]
615 public static void SetEnvironmentVariable (string variable, string value, EnvironmentVariableTarget target)
617 if (variable == null)
618 throw new ArgumentNullException ("variable");
619 if (variable == String.Empty)
620 throw new ArgumentException ("String cannot be of zero length.", "variable");
621 if (variable.IndexOf ('=') != -1)
622 throw new ArgumentException ("Environment variable name cannot contain an equal character.", "variable");
623 if (variable[0] == '\0')
624 throw new ArgumentException ("The first char in the string is the null character.", "variable");
627 case EnvironmentVariableTarget.Process:
628 InternalSetEnvironmentVariable (variable, value);
630 case EnvironmentVariableTarget.Machine:
631 if (!IsRunningOnWindows)
633 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true)) {
634 if (String.IsNullOrEmpty (value))
635 env.DeleteValue (variable, false);
637 env.SetValue (variable, value);
638 internalBroadcastSettingChange ();
641 case EnvironmentVariableTarget.User:
642 if (!IsRunningOnWindows)
644 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", true)) {
645 if (String.IsNullOrEmpty (value))
646 env.DeleteValue (variable, false);
648 env.SetValue (variable, value);
649 internalBroadcastSettingChange ();
653 throw new ArgumentException ("target");
657 [MethodImplAttribute (MethodImplOptions.InternalCall)]
658 internal static extern void InternalSetEnvironmentVariable (string variable, string value);
660 [MonoTODO ("Not implemented")]
661 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
662 public static void FailFast (string message)
664 throw new NotImplementedException ();
668 public static extern int ProcessorCount {
669 [EnvironmentPermission (SecurityAction.Demand, Read="NUMBER_OF_PROCESSORS")]
670 [MethodImplAttribute (MethodImplOptions.InternalCall)]
676 internal static bool IsRunningOnWindows {
677 get { return ((int) Platform < 4); }
681 // Used by gacutil.exe
683 #pragma warning disable 169
684 private static string GacPath {
686 if (Environment.IsRunningOnWindows) {
687 /* On windows, we don't know the path where mscorlib.dll will be installed */
688 string corlibDir = new DirectoryInfo (Path.GetDirectoryName (typeof (int).Assembly.Location)).Parent.Parent.FullName;
689 return Path.Combine (Path.Combine (corlibDir, "mono"), "gac");
692 return Path.Combine (Path.Combine (internalGetGacPath (), "mono"), "gac");
695 #pragma warning restore 169
696 [MethodImplAttribute (MethodImplOptions.InternalCall)]
697 internal extern static string internalGetGacPath ();
699 [MethodImplAttribute (MethodImplOptions.InternalCall)]
700 private extern static string [] GetLogicalDrivesInternal ();
702 [MethodImplAttribute (MethodImplOptions.InternalCall)]
703 private extern static string [] GetEnvironmentVariableNames ();
705 [MethodImplAttribute (MethodImplOptions.InternalCall)]
706 internal extern static string GetMachineConfigPath ();
708 [MethodImplAttribute (MethodImplOptions.InternalCall)]
709 internal extern static string internalGetHome ();
711 static internal bool IsUnix {
713 int platform = (int) Environment.Platform;
715 return (platform == 4 || platform == 128 || platform == 6);