Bug 23209 - Mono use of /proc not suitable for other Unix / BSD
Summary: Mono use of /proc not suitable for other Unix / BSD
Alias: None
Product: Class Libraries
Classification: Mono
Component: System ()
Version: master
Hardware: PC Other
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
Depends on:
Reported: 2014-09-19 22:25 UTC by Ben Woods
Modified: 2014-11-06 04:40 UTC (History)
2 users (show)

Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report for Bug 23209 on GitHub or Developer Community if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: GitHub Markdown or Developer Community HTML
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:

Description Ben Woods 2014-09-19 22:25:55 UTC
Mono on Linux makes use of the /proc filesystem, which is not compatible with other Unix / BSD operating systems.

I came across this problem when trying to get mediabrowser.tv to work on FreeBSD, and it gave an error /proc/net/route file not found. This reference is only made in "mcs/class/System/System.Net.NetworkInformation/IPInterfaceProperties.cs", but there are other class libraries which reference /proc.

To make mono more portable to other Unix operating systems, suggest minimising usage of /proc.
Comment 1 Ben Woods 2014-09-19 22:28:57 UTC
FreeBSD can mount Linux procfs style filesystems, but the only file in /proc/net is dev. Refer to https://www.freebsd.org/cgi/man.cgi?linprocfs(5)

OpenBSD can mount Linux procfs style filesystems with mount_procfs -o linux /proc /proc. Not sure if this provides a /proc/net/route.

Either way, it's not ideal for other Unix / BSD operating systems to have to write a Linux compatibility layer to use mono, when mono could instead use more POSIX style code.
Comment 2 Ben Woods 2014-09-19 22:31:08 UTC
I performed a search for "proc/" in the mono v3.6 code and found the following:

# grep -R proc\/ .
./man/mono.1:   echo 4096 > /proc/sys/dev/rtc/max-user-freq
./config.guess: case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
./config.guess: case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
./mono/mini/mini-amd64.c:       optimize_for_xen = access ("/proc/xen", F_OK) == 0;
./mono/mini/mini-x86.c:         optimize_for_xen = access ("/proc/xen", F_OK) == 0;
./mono/mini/mini-ppc.c: FILE* f = fopen ("/proc/self/auxv", "rb");
./mono/mini/ChangeLog:  the info in /proc/self/smaps. Avoid the assert on sigaction during
./mono/mini/ChangeLog:  better info in proc/pid/smaps these days).
./mono/mini/mini.c:     if (access ("/proc/self/maps", F_OK) != 0) {
./mono/tests/ChangeLog: * stress-runner.cs: Add 'exit-stress', add a few new /proc/pid/status entries.
./mono/tests/stress-runner.pl:  open (PROC, "</proc/$pid/status") || return undef; # might be dead already
./mono/metadata/assembly.c:     s = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
./mono/metadata/assembly.c:     str = g_strdup_printf ("/proc/%d/path/a.out", getpid ());
./mono/io-layer/processes.c:    gchar *dir = g_strdup_printf ("/proc/%d", pid);
./mono/io-layer/processes.c:            "/proc/%d/maps",        /* GNU/Linux */
./mono/io-layer/processes.c:            "/proc/%d/map",         /* FreeBSD */
./mono/io-layer/processes.c:     * for now.)  Get the info from /proc/<pid>/maps on linux,
./mono/io-layer/processes.c:     * /proc/<pid>/map on FreeBSD, other systems will have to
./mono/io-layer/processes.c:            /* No /proc/<pid>/maps so just return the main module
./mono/io-layer/processes.c:             * /proc/<pid>/maps isn't the executable, and we need
./mono/io-layer/processes.c:    filename = g_strdup_printf ("/proc/%d/psinfo", pid);
./mono/io-layer/processes.c:    filename = g_strdup_printf ("/proc/%d/exe", pid);
./mono/io-layer/processes.c:    filename = g_strdup_printf ("/proc/%d/cmdline", pid);
./mono/io-layer/processes.c:    filename = g_strdup_printf ("/proc/%d/stat", pid);
./mono/io-layer/processes.c:    /* Look up the address in /proc/<pid>/maps */
./mono/io-layer/processes.c:                    /* No /proc/<pid>/maps, so just return failure
./mono/io-layer/processes.c:                     * case where reading /proc/$pid/maps gives an
./mono/io-layer/processes.c:    /* Look up the address in /proc/<pid>/maps */
./mono/io-layer/processes.c:            /* No /proc/<pid>/maps, so just return failure
./mono/io-layer/handles.c:/* Scan /proc/<pids>/fd/ for open file descriptors to the file in
./mono/io-layer/handles.c:                      /* Look in /proc/<pid>/fd/ but ignore
./mono/io-layer/handles.c:                       * /proc/<our pid>/fd/<fd>, as we have the
./mono/io-layer/handles.c:                      g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
./mono/io-layer/handles.c:                                          "/proc/%d/fd/%s", pid,
./mono/io-layer/io.c:   fd = open ("/proc/self/mountinfo", O_RDONLY);
./mono/io-layer/io.c:           fd = open ("/proc/mounts", O_RDONLY);
./mono/io-layer/ChangeLog:      not even compile in the code that scans for /proc/fd, and go
./mono/io-layer/ChangeLog:        in /proc/<PID>/maps.
./mono/io-layer/ChangeLog:        the module passed in is NULL, search the /proc/<PID>/maps for a name that
./mono/io-layer/ChangeLog:      /proc/<pid>/maps parsing to avoid using glib functions only
./mono/utils/mono-hwcap-arm.c:   * permissions, so fall back to /proc/cpuinfo. We also
./mono/utils/mono-hwcap-arm.c:  FILE *file = fopen ("/proc/cpuinfo", "r");
./mono/utils/mono-proclib.c:    GDir *dir = g_dir_open ("/proc/", 0, NULL);
./mono/utils/mono-proclib.c:    g_snprintf (buf, sizeof (buf), "/proc/%d/status", pid);
./mono/utils/mono-proclib.c:    sprintf (fname, "/proc/%d/cmdline", GPOINTER_TO_INT (pid));
./mono/utils/mono-proclib.c: * /proc/pid/stat format:
./mono/utils/mono-proclib.c:    g_snprintf (buf, sizeof (buf), "/proc/%d/stat", pid);
./mono/utils/mono-proclib.c:    FILE *f = fopen ("/proc/stat", "r");
./mono/utils/mono-hwcap-x86.c:  mono_hwcap_x86_is_xen = !access ("/proc/xen", F_OK);
./mono/utils/mono-dl.c: binl = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
./mono/utils/ChangeLog: network statistics from "/proc/net/dev" for performance counters.
./mono/utils/mono-counters.c:   FILE *f = fopen ("/proc/loadavg", "r");
./mono/utils/mono-networkinterfaces.c:  f = fopen ("/proc/net/dev", "r");
./mono/utils/mono-networkinterfaces.c:  f = fopen ("/proc/net/dev", "r");
./mono/utils/mono-time.c:       FILE *uptime = fopen ("/proc/uptime", "r");
./mono/profiler/proflog.c:              int l = readlink ("/proc/self/exe", buf, sizeof (buf) - 1);
./mono/profiler/proflog.c:                      fprintf (stderr, "Perf syscall denied, do \"echo 1 > /proc/sys/kernel/perf_event_paranoid\" as root to enable.\n");
./mono/profiler/ChangeLog:      The issue is that while reading /proc/self/maps is can happen that
./mono/profiler/utils.c:        if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
./libgc/solaris_threads.c:      sprintf(buf, "/proc/%d", getpid());
./libgc/config.guess:   case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
./libgc/config.guess:   case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
./libgc/dyn_load.c:      sprintf(buf, "/proc/%d", getpid());
./libgc/doc/README.changes: - Changed Linux dynamic library registration code to look at /proc/self/maps
./libgc/doc/README.changes: - Fixed the /proc/self/maps code to not seek, since that apparently is not
./libgc/doc/README.changes:   /proc/self/maps after checking the __libc symbol, but before guessing.
./libgc/doc/README.changes:   /proc/self/maps so it only exists in one place (all platforms).
./libgc/doc/README.changes:   /proc/self/maps on Linux.  This ceased to be true about 2 years ago.
./libgc/doc/README.environment:GC_PRINT_ADDRESS_MAP - Linux only.  Dump /proc/self/maps, i.e. various address
./libgc/os_dep.c:/* We need to parse /proc/self/maps, either to find dynamic libraries, */
./libgc/os_dep.c: * Apply fn to a buffer containing the contents of /proc/self/maps.
./libgc/os_dep.c: * We currently do nothing to /proc/self/maps other than simply read
./libgc/os_dep.c:    /* Read /proc/self/maps, growing maps_buf as necessary.    */
./libgc/os_dep.c:           f = open("/proc/self/maps", O_RDONLY);
./libgc/os_dep.c://  GC_parse_map_entry parses an entry from /proc/self/maps so we can
./libgc/os_dep.c:#endif /* Need to parse /proc/self/maps. */
./libgc/os_dep.c:                       /* field in /proc/self/stat                     */
./libgc/os_dep.c:    /* Try to read the backing store base from /proc/self/maps.        */
./libgc/os_dep.c:    /* We read the stack base value from /proc/self/stat.  We do this  */
./libgc/os_dep.c:    f = open("/proc/self/stat", O_RDONLY);
./libgc/os_dep.c:       ABORT("Couldn't read /proc/self/stat");
./libgc/os_dep.c:    sprintf(buf, "/proc/%d", getpid());
./libgc/os_dep.c:                 ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ);
./libgc/os_dep.c:/* Dump /proc/self/maps to GC_stderr, to enable looking up names for
./libgc/pthread_support.c:    /* We look for lines "cpu<n>" in /proc/stat.                      */
./libgc/pthread_support.c:      /* entry in /proc/stat.  We identify those as           */
./libgc/pthread_support.c:    f = open("/proc/stat", O_RDONLY);
./libgc/pthread_support.c:      WARN("Couldn't read /proc/stat\n", 0);
./libgc/pthread_support.c:    /* doesn't work because the stack base in /proc/self/stat is the  */
./mcs/class/Mono.Management/Mono.Attach/VirtualMachine.cs:                      return File.OpenText ("/proc/" + pid + "/cmdline").ReadToEnd ().Split ('\0');
./mcs/class/Mono.Management/Mono.Attach/VirtualMachine.cs:                      return UnixPath.ReadLink ("/proc/" + pid + "/cwd");
./mcs/class/Managed.Windows.Forms/System.Windows.Forms/FileDialog.cs:                   // maybe check if the current user can access /proc/mounts
./mcs/class/Managed.Windows.Forms/System.Windows.Forms/FileDialog.cs:                           if (File.Exists ("/proc/mounts"))
./mcs/class/Managed.Windows.Forms/System.Windows.Forms/FileDialog.cs:                           StreamReader sr = new StreamReader ("/proc/mounts");
./mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog:           this information from /proc/mount. Updated MWFFileView to make
./mcs/class/corlib/Microsoft.Win32/UnixRegistryApi.cs:                  if (!File.Exists ("/proc/stat"))
./mcs/class/corlib/Microsoft.Win32/UnixRegistryApi.cs:                          using (StreamReader stat_file = new StreamReader ("/proc/stat", Encoding.ASCII)) {
./mcs/class/System/System.Net.NetworkInformation/ChangeLog:       and here is non-Windows version, based on /proc/net/snmp(6).
./mcs/class/System/System.Net.NetworkInformation/IPv4InterfaceProperties.cs:                            string iface_path = "/proc/sys/net/ipv4/conf/" + iface.Name + "/forwarding";
./mcs/class/System/System.Net.NetworkInformation/IPInterfaceProperties.cs:                              using (StreamReader reader = new StreamReader ("/proc/net/route")) {
./mcs/class/System/System.Net.NetworkInformation/IPGlobalProperties.cs: // It expects /proc/net/snmp (or /usr/compat/linux/proc/net/snmp),
./mcs/class/System/System.Net.NetworkInformation/IPGlobalProperties.cs:                 // There is no TCP info in /proc/net/snmp,
./mcs/class/System/System.IO/InotifyWatcher.cs:                                         using (StreamReader reader = new StreamReader ("/proc/sys/fs/inotify/max_user_watches")) {
./mcs/class/System/System.IO/InotifyWatcher.cs:                                                         "in /proc/sys/fs/inotify/max_user_watches.", nr_watches);
Comment 3 Rodrigo Kumpera 2014-10-03 08:44:40 UTC
Mono's proc usage is either a compile time switch or done at runtime by probing.

FreeBSD is currently community maintained, could you provide a patch to fix your issue here?
Comment 4 Ben Woods 2014-10-03 20:23:00 UTC
I have submitted the below pull request:

This addresses the issue I was having with /proc/net/route usage in mcs/class/System/System.Net.NetworkInformation/IPInterfaceProperties.cs.

This change obviously needs testing. I am trying to workout why I sometimes get the following exception when I run mono with --trace=N:nothing:
EXCEPTION handling: System.ArgumentNullException: Argument cannot be null.
Comment 5 Ben Woods 2014-10-29 10:35:00 UTC
I have determined that the System.ArgumentNullException I received was unrelated to this patch. It was due to the locale not being set on my system, causing get_posix_locale to return NULL in mono/mono/metadata/locales.c, which was caught in mcs/class/corlib/System.Globalization/CultureInfo.cs.

This patch therefore works fine on FreeBSD - think we can merge this pull request?
Comment 6 Ben Woods 2014-11-06 04:40:55 UTC
I have found another Linuxism which was preventing the mono networking code from working on FreeBSD. This is separate to the bug fixed in my pull request above (both fixes are required).

My new pull request is here: https://github.com/mono/mono/pull/1390

In summary, Mono on FreeBSD will currently use the mono Linux NetworkInterface code, which contains some Linuxisms (such as parsing files in /sys/class/net/). Refer to the problem code here:

This change causes mono on FreeBSD to use the Mac NetworkInterface code instead, which works fine. Refer to the above link which shows class MacOsNetworkInterface : UnixNetworkInterface.