FileSystemWatcher Issue?

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

FileSystemWatcher Issue?

Rick Tillery
In conjunction with "Sync of mono Cert Store," I'm trying to utilize FileSystemWatcher to monitor the system certificate file for changes (which will trigger a cert-sync).

Since some distros use the legacy address (/etc/pki/tls/certs/ca-bundle.crt) as a symbolic link, and modification of the system cert store appears to be a free-for-all between using the update-ca-trust or update-ca-certificates scripts, running other utilities, modifying the file directly, and even replacing the file completely, I have been testing as many file manipulation combinations as I can imagine, to ensure FileSystemWatcher catches them all.

In doing so, I found an issue in FileSystemWatcher.

To handle the symbolic link case, FileSystemWatcher objects are set up for each file in the chain, symbolic link and target alike.  In one test, the target of a symbolic link is replaced by another file using mv.  But FileSystemWatcher does not register the change.  Oddly, if only a watch of the target file is registered, FileSystemWatcher registers the change.

The code I'm using is attached.  Here is what I see on command line:

(console1) ~$ echo >bar "test"
(console1) ~$ echo >foobar "test1"
(console1) ~$ ln -s bar foo
(console1) ~$ ll
total 24
-rw-rw-r--. 1 rtillery rtillery      5 Jul 31 11:06 bar
lrwxrwxrwx. 1 rtillery rtillery      3 Jul 31 11:06 foo -> bar
-rw-rw-r--. 1 rtillery rtillery      6 Jul 31 11:06 foobar
-rwxrwxr-x. 1 rtillery rtillery   5632 Jul 31 11:05 fsw.exe
(console1) ~$ mono fsw.exe ./bar
Watching: /home/rtillery/bar
bar watching ENABLED
Press 'Q' to quit

(console2) ~$ mv foobar bar

(console1)
File: /home/rtillery/foobar renamed to /home/rtillery/bar

...

(console1) ~$ mono fsw.exe ./foo
Watching: /home/rtillery/foo
Watching: /home/rtillery/bar
foo watching ENABLED
bar watching ENABLED
Press 'Q' to quit

(console2) ~$ echo >foobar "test"
(console2) ~$ mv foobar bar

Nothing appears on console 1 in the second case, but I don't really see why.  I believe I'm setting up the watches in exactly the same way, except that there are two instead of one.  Is this a problem with FileSystemWatcher?  Code is below.

Thanks,
Rick

using System;
using System.Collections.Generic;
using System.IO;
using Mono.Unix;

// Compile with "mcs fsw.cs /r:Mono.Posix.dll"

public class FSW
{
    private static IDictionary<string, FileSystemWatcher> watches = new Dictionary<string, FileSystemWatcher>();

    public static void Main()
    {
        string[] args = System.Environment.GetCommandLineArgs();
        if (args.Length != 2)
        {
            System.Console.WriteLine("USAGE: (mono) fswtest(.exe) file_to_watch");
            goto Error;
        }

        SetUpWatch(args[1]);

        Console.WriteLine("Press \'Q\' to quit.");
        while(Console.Read() != 'q')
            ;

    Error:
        ;
    }

    private static void SetUpWatch(string file)
    {
        do
        {
            AddWatch(file);
            file = GetSymbolicLinkTarget(file);
        }
        while (file != null);
        EnableWatches();
    }

    private static void AddWatch(string file)
    {
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = Path.GetDirectoryName(file);
        watcher.NotifyFilter = 0
//          | NotifyFilters.Attributes
//          | NotifyFilters.CreationTime
          | NotifyFilters.DirectoryName
          | NotifyFilters.FileName
          | NotifyFilters.LastAccess
          | NotifyFilters.LastWrite
//          | NotifyFilters.Security
//          | NotifyFilters.Size
          ;
        watcher.Filter = Path.GetFileName(file);
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Created += new FileSystemEventHandler(OnChanged);
        watcher.Deleted += new FileSystemEventHandler(OnChanged);
//        watcher.Disposed // Occurs when the component is disposed by a call to the Dispose method. (Inherited from Component.)
//        watcher.Error // Occurs when the instance of FileSystemWatcher is unable to continue monitoring changes or when the internal buffer overflows.
        watcher.Renamed += new RenamedEventHandler(OnRenamed);
        watches.Add(Path.GetFullPath(file), watcher);
Console.WriteLine("Watching: " + Path.GetFullPath(file));
    }

    private static string GetSymbolicLinkTarget(string file)
    {
        UnixSymbolicLinkInfo link = new UnixSymbolicLinkInfo(file);
        return link.HasContents ? link.GetContents().FullName : null;
    }

    private static void EnableWatches()
    {
        foreach (KeyValuePair<string, FileSystemWatcher> watch in watches)
        {
            watch.Value.EnableRaisingEvents = true;
Console.WriteLine("" + watch.Value.Filter + " watching ENABLED");
        }
    }

    private static void OnChanged(object source, FileSystemEventArgs eventargs)
    {
        Console.WriteLine("File: \"" + eventargs.FullPath + "\" -- " + eventargs.ChangeType);
    }

    private static void OnRenamed(object source, RenamedEventArgs eventargs)
    {
        Console.WriteLine("File: {0} renamed to {1}", eventargs.OldFullPath, eventargs.FullPath);
    }
}

_______________________________________________
Mono-devel-list mailing list
[hidden email]
http://lists.dot.net/mailman/listinfo/mono-devel-list

fsw.cs (3K) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: FileSystemWatcher Issue?

Rick Tillery
I'm sorry, I neglected to specify the environments:

mono 5.0.1.1
CentOS 7 & Ubuntu 16.04

Rick

On Mon, Jul 31, 2017 at 11:46 AM, Rick Tillery <[hidden email]> wrote:
In conjunction with "Sync of mono Cert Store," I'm trying to utilize FileSystemWatcher to monitor the system certificate file for changes (which will trigger a cert-sync).

Since some distros use the legacy address (/etc/pki/tls/certs/ca-bundle.crt) as a symbolic link, and modification of the system cert store appears to be a free-for-all between using the update-ca-trust or update-ca-certificates scripts, running other utilities, modifying the file directly, and even replacing the file completely, I have been testing as many file manipulation combinations as I can imagine, to ensure FileSystemWatcher catches them all.

In doing so, I found an issue in FileSystemWatcher.

To handle the symbolic link case, FileSystemWatcher objects are set up for each file in the chain, symbolic link and target alike.  In one test, the target of a symbolic link is replaced by another file using mv.  But FileSystemWatcher does not register the change.  Oddly, if only a watch of the target file is registered, FileSystemWatcher registers the change.

The code I'm using is attached.  Here is what I see on command line:

(console1) ~$ echo >bar "test"
(console1) ~$ echo >foobar "test1"
(console1) ~$ ln -s bar foo
(console1) ~$ ll
total 24
-rw-rw-r--. 1 rtillery rtillery      5 Jul 31 11:06 bar
lrwxrwxrwx. 1 rtillery rtillery      3 Jul 31 11:06 foo -> bar
-rw-rw-r--. 1 rtillery rtillery      6 Jul 31 11:06 foobar
-rwxrwxr-x. 1 rtillery rtillery   5632 Jul 31 11:05 fsw.exe
(console1) ~$ mono fsw.exe ./bar
Watching: /home/rtillery/bar
bar watching ENABLED
Press 'Q' to quit

(console2) ~$ mv foobar bar

(console1)
File: /home/rtillery/foobar renamed to /home/rtillery/bar

...

(console1) ~$ mono fsw.exe ./foo
Watching: /home/rtillery/foo
Watching: /home/rtillery/bar
foo watching ENABLED
bar watching ENABLED
Press 'Q' to quit

(console2) ~$ echo >foobar "test"
(console2) ~$ mv foobar bar

Nothing appears on console 1 in the second case, but I don't really see why.  I believe I'm setting up the watches in exactly the same way, except that there are two instead of one.  Is this a problem with FileSystemWatcher?  Code is below.

Thanks,
Rick

using System;
using System.Collections.Generic;
using System.IO;
using Mono.Unix;

// Compile with "mcs fsw.cs /r:Mono.Posix.dll"

public class FSW
{
    private static IDictionary<string, FileSystemWatcher> watches = new Dictionary<string, FileSystemWatcher>();

    public static void Main()
    {
        string[] args = System.Environment.GetCommandLineArgs();
        if (args.Length != 2)
        {
            System.Console.WriteLine("USAGE: (mono) fswtest(.exe) file_to_watch");
            goto Error;
        }

        SetUpWatch(args[1]);

        Console.WriteLine("Press \'Q\' to quit.");
        while(Console.Read() != 'q')
            ;

    Error:
        ;
    }

    private static void SetUpWatch(string file)
    {
        do
        {
            AddWatch(file);
            file = GetSymbolicLinkTarget(file);
        }
        while (file != null);
        EnableWatches();
    }

    private static void AddWatch(string file)
    {
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = Path.GetDirectoryName(file);
        watcher.NotifyFilter = 0
//          | NotifyFilters.Attributes
//          | NotifyFilters.CreationTime
          | NotifyFilters.DirectoryName
          | NotifyFilters.FileName
          | NotifyFilters.LastAccess
          | NotifyFilters.LastWrite
//          | NotifyFilters.Security
//          | NotifyFilters.Size
          ;
        watcher.Filter = Path.GetFileName(file);
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Created += new FileSystemEventHandler(OnChanged);
        watcher.Deleted += new FileSystemEventHandler(OnChanged);
//        watcher.Disposed // Occurs when the component is disposed by a call to the Dispose method. (Inherited from Component.)
//        watcher.Error // Occurs when the instance of FileSystemWatcher is unable to continue monitoring changes or when the internal buffer overflows.
        watcher.Renamed += new RenamedEventHandler(OnRenamed);
        watches.Add(Path.GetFullPath(file), watcher);
Console.WriteLine("Watching: " + Path.GetFullPath(file));
    }

    private static string GetSymbolicLinkTarget(string file)
    {
        UnixSymbolicLinkInfo link = new UnixSymbolicLinkInfo(file);
        return link.HasContents ? link.GetContents().FullName : null;
    }

    private static void EnableWatches()
    {
        foreach (KeyValuePair<string, FileSystemWatcher> watch in watches)
        {
            watch.Value.EnableRaisingEvents = true;
Console.WriteLine("" + watch.Value.Filter + " watching ENABLED");
        }
    }

    private static void OnChanged(object source, FileSystemEventArgs eventargs)
    {
        Console.WriteLine("File: \"" + eventargs.FullPath + "\" -- " + eventargs.ChangeType);
    }

    private static void OnRenamed(object source, RenamedEventArgs eventargs)
    {
        Console.WriteLine("File: {0} renamed to {1}", eventargs.OldFullPath, eventargs.FullPath);
    }
}


_______________________________________________
Mono-devel-list mailing list
[hidden email]
http://lists.dot.net/mailman/listinfo/mono-devel-list
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: FileSystemWatcher Issue?

Rick Tillery
After some additional tests, I found that the order the watches are enabled makes a difference.  After modifying the following line of code in EnableWatches():

       foreach (KeyValuePair<string, FileSystemWatcher> watch in watches)

to

       foreach (KeyValuePair<string, FileSystemWatcher> watch in watches.Reverse())

(and adding "using System.Linq"), which causes the target file (bar) watch to be enabled before the link file (foo) watch, I found that copying over the link file with another file (mv foobar foo) does not register an event (it did before), but copying over the target (mv foobar bar) does (it did not before).

I haven't found a way to make both work at the same time, yet.  But I have not found another combination that fails to generate an event, no matter which one is enabled first.
(e.g. "cp foobar foo", "cp foobar bar", "rm foo", "rm bar", "mv foo foobar", "mv bar foobar", "echo >foo 'test'", "echo >bar 'test'", "echo >>foo 'test'", "echo >>bar 'test'", "touch foo", "touch bar", "ln -s foobar bar", "vi foo" :w, "vi bar" :w, "nano foo" ctrl-o, "nano bar" ctrl-o, "gedit foo" save, "gedit bar" save, etc.) 

Is there something I'm doing wrong here?

Rick


On Mon, Jul 31, 2017 at 1:22 PM, Rick Tillery <[hidden email]> wrote:
I'm sorry, I neglected to specify the environments:

mono 5.0.1.1
CentOS 7 & Ubuntu 16.04

Rick

On Mon, Jul 31, 2017 at 11:46 AM, Rick Tillery <[hidden email]> wrote:
In conjunction with "Sync of mono Cert Store," I'm trying to utilize FileSystemWatcher to monitor the system certificate file for changes (which will trigger a cert-sync).

Since some distros use the legacy address (/etc/pki/tls/certs/ca-bundle.crt) as a symbolic link, and modification of the system cert store appears to be a free-for-all between using the update-ca-trust or update-ca-certificates scripts, running other utilities, modifying the file directly, and even replacing the file completely, I have been testing as many file manipulation combinations as I can imagine, to ensure FileSystemWatcher catches them all.

In doing so, I found an issue in FileSystemWatcher.

To handle the symbolic link case, FileSystemWatcher objects are set up for each file in the chain, symbolic link and target alike.  In one test, the target of a symbolic link is replaced by another file using mv.  But FileSystemWatcher does not register the change.  Oddly, if only a watch of the target file is registered, FileSystemWatcher registers the change.

The code I'm using is attached.  Here is what I see on command line:

(console1) ~$ echo >bar "test"
(console1) ~$ echo >foobar "test1"
(console1) ~$ ln -s bar foo
(console1) ~$ ll
total 24
-rw-rw-r--. 1 rtillery rtillery      5 Jul 31 11:06 bar
lrwxrwxrwx. 1 rtillery rtillery      3 Jul 31 11:06 foo -> bar
-rw-rw-r--. 1 rtillery rtillery      6 Jul 31 11:06 foobar
-rwxrwxr-x. 1 rtillery rtillery   5632 Jul 31 11:05 fsw.exe
(console1) ~$ mono fsw.exe ./bar
Watching: /home/rtillery/bar
bar watching ENABLED
Press 'Q' to quit

(console2) ~$ mv foobar bar

(console1)
File: /home/rtillery/foobar renamed to /home/rtillery/bar

...

(console1) ~$ mono fsw.exe ./foo
Watching: /home/rtillery/foo
Watching: /home/rtillery/bar
foo watching ENABLED
bar watching ENABLED
Press 'Q' to quit

(console2) ~$ echo >foobar "test"
(console2) ~$ mv foobar bar

Nothing appears on console 1 in the second case, but I don't really see why.  I believe I'm setting up the watches in exactly the same way, except that there are two instead of one.  Is this a problem with FileSystemWatcher?  Code is below.

Thanks,
Rick

using System;
using System.Collections.Generic;
using System.IO;
using Mono.Unix;

// Compile with "mcs fsw.cs /r:Mono.Posix.dll"

public class FSW
{
    private static IDictionary<string, FileSystemWatcher> watches = new Dictionary<string, FileSystemWatcher>();

    public static void Main()
    {
        string[] args = System.Environment.GetCommandLineArgs();
        if (args.Length != 2)
        {
            System.Console.WriteLine("USAGE: (mono) fswtest(.exe) file_to_watch");
            goto Error;
        }

        SetUpWatch(args[1]);

        Console.WriteLine("Press \'Q\' to quit.");
        while(Console.Read() != 'q')
            ;

    Error:
        ;
    }

    private static void SetUpWatch(string file)
    {
        do
        {
            AddWatch(file);
            file = GetSymbolicLinkTarget(file);
        }
        while (file != null);
        EnableWatches();
    }

    private static void AddWatch(string file)
    {
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = Path.GetDirectoryName(file);
        watcher.NotifyFilter = 0
//          | NotifyFilters.Attributes
//          | NotifyFilters.CreationTime
          | NotifyFilters.DirectoryName
          | NotifyFilters.FileName
          | NotifyFilters.LastAccess
          | NotifyFilters.LastWrite
//          | NotifyFilters.Security
//          | NotifyFilters.Size
          ;
        watcher.Filter = Path.GetFileName(file);
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Created += new FileSystemEventHandler(OnChanged);
        watcher.Deleted += new FileSystemEventHandler(OnChanged);
//        watcher.Disposed // Occurs when the component is disposed by a call to the Dispose method. (Inherited from Component.)
//        watcher.Error // Occurs when the instance of FileSystemWatcher is unable to continue monitoring changes or when the internal buffer overflows.
        watcher.Renamed += new RenamedEventHandler(OnRenamed);
        watches.Add(Path.GetFullPath(file), watcher);
Console.WriteLine("Watching: " + Path.GetFullPath(file));
    }

    private static string GetSymbolicLinkTarget(string file)
    {
        UnixSymbolicLinkInfo link = new UnixSymbolicLinkInfo(file);
        return link.HasContents ? link.GetContents().FullName : null;
    }

    private static void EnableWatches()
    {
        foreach (KeyValuePair<string, FileSystemWatcher> watch in watches)
        {
            watch.Value.EnableRaisingEvents = true;
Console.WriteLine("" + watch.Value.Filter + " watching ENABLED");
        }
    }

    private static void OnChanged(object source, FileSystemEventArgs eventargs)
    {
        Console.WriteLine("File: \"" + eventargs.FullPath + "\" -- " + eventargs.ChangeType);
    }

    private static void OnRenamed(object source, RenamedEventArgs eventargs)
    {
        Console.WriteLine("File: {0} renamed to {1}", eventargs.OldFullPath, eventargs.FullPath);
    }
}



_______________________________________________
Mono-devel-list mailing list
[hidden email]
http://lists.dot.net/mailman/listinfo/mono-devel-list
Loading...