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;
49 public static class Environment {
51 public sealed class Environment {
53 private Environment ()
58 * This is the version number of the corlib-runtime interface. When
59 * making changes to this interface (by changing the layout
60 * of classes the runtime knows about, changing icall signature or
61 * semantics etc), increment this variable. Also increment the
62 * pair of this variable in the runtime in metadata/appdomain.c.
63 * Changes which are already detected at runtime, like the addition
64 * of icalls, do not require an increment.
66 private const int mono_corlib_version = 64;
68 public enum SpecialFolder
69 { // TODO: Determine if these windoze style folder identifiers
70 // have unix/linux counterparts
86 DesktopDirectory = 0x10,
88 ApplicationData = 0x1a,
89 LocalApplicationData = 0x1c,
93 CommonApplicationData = 0x23,
97 CommonProgramFiles = 0x2b,
101 /// Gets the command line for this process
103 public static string CommandLine {
104 // note: security demand inherited from calling GetCommandLineArgs
106 // FIXME: we may need to quote, but any sane person
107 // should use GetCommandLineArgs () instead.
108 return String.Join (" ", GetCommandLineArgs ());
113 /// Gets or sets the current directory. Actually this is supposed to get
114 /// and/or set the process start directory acording to the documentation
115 /// but actually test revealed at beta2 it is just Getting/Setting the CurrentDirectory
117 public static string CurrentDirectory
120 return Directory.GetCurrentDirectory ();
123 Directory.SetCurrentDirectory (value);
128 /// Gets or sets the exit code of this process
130 public extern static int ExitCode
132 [MethodImplAttribute (MethodImplOptions.InternalCall)]
134 [MethodImplAttribute (MethodImplOptions.InternalCall)]
141 public extern bool HasShutdownStarted
143 [MethodImplAttribute (MethodImplOptions.InternalCall)]
149 /// Gets the name of the local computer
151 public extern static string MachineName {
152 [MethodImplAttribute (MethodImplOptions.InternalCall)]
153 [EnvironmentPermission (SecurityAction.Demand, Read="COMPUTERNAME")]
154 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
159 /// Gets the standard new line value
161 public extern static string NewLine {
162 [MethodImplAttribute (MethodImplOptions.InternalCall)]
167 // Support methods and fields for OSVersion property
169 static OperatingSystem os;
171 internal static extern PlatformID Platform {
172 [MethodImplAttribute (MethodImplOptions.InternalCall)]
176 [MethodImplAttribute (MethodImplOptions.InternalCall)]
177 internal static extern string GetOSVersionString ();
180 /// Gets the current OS version information
182 public static OperatingSystem OSVersion {
185 Version v = Version.CreateFromString (GetOSVersionString ());
186 PlatformID p = Platform;
191 os = new OperatingSystem (p, v);
200 public static string StackTrace {
201 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
203 System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace (0, true);
204 return trace.ToString ();
209 /// Get a fully qualified path to the system directory
211 public static string SystemDirectory {
213 return GetFolderPath (SpecialFolder.System);
218 /// Get the number of milliseconds that have elapsed since the system was booted
220 public extern static int TickCount {
221 [MethodImplAttribute (MethodImplOptions.InternalCall)]
226 /// Get UserDomainName
228 public static string UserDomainName {
229 // FIXME: this variable doesn't exist (at least not on WinXP) - reported to MS as FDBK20562
230 [EnvironmentPermission (SecurityAction.Demand, Read="USERDOMAINNAME")]
237 /// Gets a flag indicating whether the process is in interactive mode
239 [MonoTODO ("Currently always returns false, regardless of interactive state")]
240 public static bool UserInteractive {
247 /// Get the user name of current process is running under
249 public extern static string UserName {
250 [MethodImplAttribute (MethodImplOptions.InternalCall)]
251 [EnvironmentPermission (SecurityAction.Demand, Read="USERNAME;USER")]
256 /// Get the version of the common language runtime
258 public static Version Version {
260 return new Version (Consts.FxFileVersion);
265 /// Get the amount of physical memory mapped to process
267 [MonoTODO ("Currently always returns zero")]
268 public static long WorkingSet {
269 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
273 [MethodImplAttribute (MethodImplOptions.InternalCall)]
274 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
275 public extern static void Exit (int exitCode);
278 /// Substitute environment variables in the argument "name"
280 public static string ExpandEnvironmentVariables (string name)
283 throw new ArgumentNullException ("name");
285 int off1 = name.IndexOf ('%');
289 int len = name.Length;
291 if (off1 == len - 1 || (off2 = name.IndexOf ('%', off1 + 1)) == -1)
294 StringBuilder result = new StringBuilder ();
295 result.Append (name, 0, off1);
296 Hashtable tbl = null;
298 string var = name.Substring (off1 + 1, off2 - off1 - 1);
299 string value = GetEnvironmentVariable (var);
300 if (value == null && Environment.IsRunningOnWindows) {
301 // On windows, env. vars. are case insensitive
303 tbl = GetEnvironmentVariablesNoCase ();
305 value = tbl [var] as string;
308 // If value not found, add %FOO to stream,
309 // and use the closing % for the next iteration.
310 // If value found, expand it in place of %FOO%
316 result.Append (value);
319 off1 = name.IndexOf ('%', off2 + 1);
320 // If no % found for off1, don't look for one for off2
321 off2 = (off1 == -1 || off2 > len-1)? -1 :name.IndexOf ('%', off1 + 1);
322 // textLen is the length of text between the closing % of current iteration
323 // and the starting % of the next iteration if any. This text is added to output
325 // If no new % found, use all the remaining text
326 if (off1 == -1 || off2 == -1)
327 textLen = len - oldOff2 - 1;
328 // If value found in current iteration, use text after current closing % and next %
329 else if(value != null)
330 textLen = off1 - oldOff2 - 1;
331 // If value not found in current iteration, but a % was found for next iteration,
332 // use text from current closing % to the next %.
334 textLen = off1 - oldOff2;
335 if(off1 >= oldOff2 || off1 == -1)
336 result.Append (name, oldOff2+1, textLen);
337 } while (off2 > -1 && off2 < len);
339 return result.ToString ();
344 /// Return an array of the command line arguments of the current process
346 [MethodImplAttribute (MethodImplOptions.InternalCall)]
347 [EnvironmentPermissionAttribute (SecurityAction.Demand, Read = "PATH")]
348 public extern static string[] GetCommandLineArgs ();
350 [MethodImplAttribute (MethodImplOptions.InternalCall)]
351 internal extern static string internalGetEnvironmentVariable (string name);
354 /// Return a string containing the value of the environment
355 /// variable identifed by parameter "variable"
357 public static string GetEnvironmentVariable (string name)
359 if (SecurityManager.SecurityEnabled) {
360 new EnvironmentPermission (EnvironmentPermissionAccess.Read, name).Demand ();
362 return internalGetEnvironmentVariable (name);
365 static Hashtable GetEnvironmentVariablesNoCase ()
367 Hashtable vars = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
368 CaseInsensitiveComparer.Default);
370 foreach (string name in GetEnvironmentVariableNames ()) {
371 vars [name] = internalGetEnvironmentVariable (name);
378 /// Return a set of all environment variables and their values
381 public static IDictionary GetEnvironmentVariables ()
383 StringBuilder sb = null;
384 if (SecurityManager.SecurityEnabled) {
385 // we must have access to each variable to get the lot
386 sb = new StringBuilder ();
387 // but (performance-wise) we do not want a stack-walk
388 // for each of them so we concatenate them
391 Hashtable vars = new Hashtable ();
392 foreach (string name in GetEnvironmentVariableNames ()) {
393 vars [name] = internalGetEnvironmentVariable (name);
401 new EnvironmentPermission (EnvironmentPermissionAccess.Read, sb.ToString ()).Demand ();
406 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
407 public static IDictionary GetEnvironmentVariables ()
409 Hashtable vars = new Hashtable ();
410 foreach (string name in GetEnvironmentVariableNames ()) {
411 vars [name] = internalGetEnvironmentVariable (name);
417 [MethodImplAttribute (MethodImplOptions.InternalCall)]
418 private extern static string GetWindowsFolderPath (int folder);
421 /// Returns the fully qualified path of the
422 /// folder specified by the "folder" parameter
424 public static string GetFolderPath (SpecialFolder folder)
428 if (Environment.IsRunningOnWindows) {
429 dir = GetWindowsFolderPath ((int) folder);
431 dir = InternalGetFolderPath (folder);
434 if ((dir != null) && (dir.Length > 0) && SecurityManager.SecurityEnabled) {
435 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dir).Demand ();
440 private static string ReadXdgUserDir (string config_dir, string home_dir,
441 string key, string fallback)
443 string env_path = internalGetEnvironmentVariable (key);
444 if (env_path != null && env_path != String.Empty) {
448 string user_dirs_path = Path.Combine (config_dir, "user-dirs.dirs");
450 if (!File.Exists (user_dirs_path)) {
451 return Path.Combine (home_dir, fallback);
455 using(StreamReader reader = new StreamReader (user_dirs_path)) {
457 while ((line = reader.ReadLine ()) != null) {
459 int delim_index = line.IndexOf ('=');
460 if(delim_index > 8 && line.Substring (0, delim_index) == key) {
461 string path = line.Substring (delim_index + 1).Trim ('"');
462 bool relative = false;
464 if (path.StartsWith ("$HOME/")) {
466 path = path.Substring (6);
467 } else if (!path.StartsWith ("/")) {
471 return relative ? Path.Combine (home_dir, path) : path;
475 } catch (FileNotFoundException) {
478 return Path.Combine (home_dir, fallback);
482 // the security runtime (and maybe other parts of corlib) needs the
483 // information to initialize themselves before permissions can be checked
484 internal static string InternalGetFolderPath (SpecialFolder folder)
486 string home = internalGetHome ();
488 // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
490 // note: skip security check for environment variables
491 string data = internalGetEnvironmentVariable ("XDG_DATA_HOME");
492 if ((data == null) || (data == String.Empty)) {
493 data = Path.Combine (home, ".local");
494 data = Path.Combine (data, "share");
497 // note: skip security check for environment variables
498 string config = internalGetEnvironmentVariable ("XDG_CONFIG_HOME");
499 if ((config == null) || (config == String.Empty)) {
500 config = Path.Combine (home, ".config");
505 // MyComputer is a virtual directory
506 case SpecialFolder.MyComputer:
510 case SpecialFolder.Personal:
512 // use FDO's CONFIG_HOME. This data will be synced across a network like the windows counterpart.
513 case SpecialFolder.ApplicationData:
515 //use FDO's DATA_HOME. This is *NOT* synced
516 case SpecialFolder.LocalApplicationData:
519 case SpecialFolder.Desktop:
521 case SpecialFolder.DesktopDirectory:
522 return ReadXdgUserDir (config, home, "XDG_DESKTOP_DIR", "Desktop");
524 case SpecialFolder.MyMusic:
525 return ReadXdgUserDir (config, home, "XDG_MUSIC_DIR", "Music");
527 case SpecialFolder.MyPictures:
528 return ReadXdgUserDir (config, home, "XDG_PICTURES_DIR", "Pictures");
530 // these simply dont exist on Linux
531 // The spec says if a folder doesnt exist, we
533 case SpecialFolder.Favorites:
534 case SpecialFolder.Programs:
535 case SpecialFolder.SendTo:
536 case SpecialFolder.StartMenu:
537 case SpecialFolder.Startup:
538 case SpecialFolder.Templates:
539 case SpecialFolder.Cookies:
540 case SpecialFolder.History:
541 case SpecialFolder.InternetCache:
542 case SpecialFolder.Recent:
543 case SpecialFolder.CommonProgramFiles:
544 case SpecialFolder.ProgramFiles:
545 case SpecialFolder.System:
547 // This is where data common to all users goes
548 case SpecialFolder.CommonApplicationData:
551 throw new ArgumentException ("Invalid SpecialFolder");
555 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
556 public static string[] GetLogicalDrives ()
558 return GetLogicalDrivesInternal ();
561 // FIXME: Anyone using this anywhere ?
562 static internal string GetResourceString (string s) { return String.Empty; }
566 public static string GetEnvironmentVariable (string variable, EnvironmentVariableTarget target)
569 case EnvironmentVariableTarget.Process:
570 return GetEnvironmentVariable (variable);
571 case EnvironmentVariableTarget.Machine:
572 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
573 if (!IsRunningOnWindows)
575 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
576 return env.GetValue (variable).ToString ();
578 case EnvironmentVariableTarget.User:
579 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
580 if (!IsRunningOnWindows)
582 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", false)) {
583 return env.GetValue (variable).ToString ();
586 throw new ArgumentException ("target");
590 public static IDictionary GetEnvironmentVariables (EnvironmentVariableTarget target)
592 IDictionary variables = (IDictionary)new Hashtable ();
594 case EnvironmentVariableTarget.Process:
595 variables = GetEnvironmentVariables ();
597 case EnvironmentVariableTarget.Machine:
598 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
599 if (IsRunningOnWindows) {
600 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
601 string[] value_names = env.GetValueNames ();
602 foreach (string value_name in value_names)
603 variables.Add (value_name, env.GetValue (value_name));
607 case EnvironmentVariableTarget.User:
608 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
609 if (IsRunningOnWindows) {
610 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment")) {
611 string[] value_names = env.GetValueNames ();
612 foreach (string value_name in value_names)
613 variables.Add (value_name, env.GetValue (value_name));
618 throw new ArgumentException ("target");
623 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
624 public static void SetEnvironmentVariable (string variable, string value)
626 SetEnvironmentVariable (variable, value, EnvironmentVariableTarget.Process);
629 [EnvironmentPermission (SecurityAction.Demand, Unrestricted = true)]
630 public static void SetEnvironmentVariable (string variable, string value, EnvironmentVariableTarget target)
632 if (variable == null)
633 throw new ArgumentNullException ("variable");
634 if (variable == String.Empty)
635 throw new ArgumentException ("String cannot be of zero length.", "variable");
636 if (variable.IndexOf ('=') != -1)
637 throw new ArgumentException ("Environment variable name cannot contain an equal character.", "variable");
638 if (variable[0] == '\0')
639 throw new ArgumentException ("The first char in the string is the null character.", "variable");
642 case EnvironmentVariableTarget.Process:
643 InternalSetEnvironmentVariable (variable, value);
645 case EnvironmentVariableTarget.Machine:
646 if (!IsRunningOnWindows)
648 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true)) {
649 if (value == null || value.Length == 0)
650 env.DeleteValue (variable, false);
652 env.SetValue (variable, value);
655 case EnvironmentVariableTarget.User:
656 if (!IsRunningOnWindows)
658 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", true)) {
659 if (value == null || value.Length == 0)
660 env.DeleteValue (variable, false);
662 env.SetValue (variable, value);
666 throw new ArgumentException ("target");
670 [MethodImplAttribute (MethodImplOptions.InternalCall)]
671 internal static extern void InternalSetEnvironmentVariable (string variable, string value);
673 public static extern int ProcessorCount {
674 [EnvironmentPermission (SecurityAction.Demand, Read="NUMBER_OF_PROCESSORS")]
675 [MethodImplAttribute (MethodImplOptions.InternalCall)]
679 [MonoTODO ("Not implemented")]
680 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
681 public static void FailFast (string message)
683 throw new NotImplementedException ();
689 internal static bool IsRunningOnWindows {
690 get { return ((int) Platform != 128); }
693 private static string GacPath {
695 if (Environment.IsRunningOnWindows) {
696 /* On windows, we don't know the path where mscorlib.dll will be installed */
697 string corlibDir = new DirectoryInfo (Path.GetDirectoryName (typeof (int).Assembly.Location)).Parent.Parent.FullName;
698 return Path.Combine (Path.Combine (corlibDir, "mono"), "gac");
701 return Path.Combine (Path.Combine (internalGetGacPath (), "mono"), "gac");
705 [MethodImplAttribute (MethodImplOptions.InternalCall)]
706 private extern static string [] GetLogicalDrivesInternal ();
708 [MethodImplAttribute (MethodImplOptions.InternalCall)]
709 private extern static string [] GetEnvironmentVariableNames ();
711 [MethodImplAttribute (MethodImplOptions.InternalCall)]
712 internal extern static string GetMachineConfigPath ();
714 [MethodImplAttribute (MethodImplOptions.InternalCall)]
715 internal extern static string internalGetGacPath ();
717 [MethodImplAttribute (MethodImplOptions.InternalCall)]
718 internal extern static string internalGetHome ();