July 08, 2016

Look and LISTEN

To find all listening deamons on your box use the lsof command and grep for LISTEN. See the lsof man page for details. So what's running on your machine?

$ lsof -i | grep LISTEN
SpotifyWe   427 bob    6u  IPv4 0xfadf3f229c048281      0t0  TCP localhost:4370 (LISTEN)
SpotifyWe   427 bob    7u  IPv4 0xfadf3f229c048b79      0t0  TCP localhost:4380 (LISTEN)
NDSPCShow 13355 bob   45u  IPv4 0xfadf3f22ae9d6ea1      0t0  TCP localhost:9012 (LISTEN)
NDSPCShow 13355 bob   46u  IPv4 0xfadf3f22af352b79      0t0  TCP localhost:dynamid (LISTEN)
Google    98152 bob  219u  IPv4 0xfadf3f22a1e07091      0t0  TCP 192.168.87.102:freeciv (LISTEN)
Google    98152 bob  225u  IPv4 0xfadf3f22a26b6091      0t0  TCP localhost:freeciv (LISTEN)

This shows that I currently have 6 listening deamons connected to 6 ports. All of these are TCP deamons. The NDSPCShow with PID 13355 is a process I'd like to kill and ensure that it never starts again. I dunno what this process actually does nor can I find any information regarding it.

Lsof uses the keywords LISTEN, ESTABLISHED, TCP, UDP etc to display the type and status of the connection. Check man lsof for details. But lsof is not the only program that shows this status netstat is another option. To see the processes that uses a socket connection on your machine.

$ netstat -an | grep LISTEN
tcp6       0      0  ::1.587                *.*                    LISTEN     
tcp4       0      0  127.0.0.1.587          *.*                    LISTEN     
tcp6       0      0  ::1.25                 *.*                    LISTEN     
tcp4       0      0  127.0.0.1.25           *.*                    LISTEN     
tcp4       0      0  127.0.0.1.5556         *.*                    LISTEN     
tcp4       0      0  192.168.87.102.5556    *.*                    LISTEN     
tcp4       0      0  127.0.0.1.9002         *.*                    LISTEN     
tcp4       0      0  127.0.0.1.9012         *.*                    LISTEN     
tcp4       0      0  127.0.0.1.4380         *.*                    LISTEN     
tcp4       0      0  127.0.0.1.4370         *.*                    LISTEN     
tcp4       0      0  *.88                   *.*                    LISTEN     
tcp6       0      0  *.88                   *.*                    LISTEN     
tcp4       0      0  127.0.0.1.49153        *.*                    LISTEN     
tcp4       0      0  127.0.0.1.49152        *.*                    LISTEN     
tcp4       0      0  127.0.0.1.3310         *.*                    LISTEN     
tcp46      0      0  *.80                   *.*                    LISTEN     
tcp4       0      0  *.445                  *.*                    LISTEN     
tcp6       0      0  *.445                  *.*                    LISTEN     
tcp4       0      0  *.5900                 *.*                    LISTEN     
tcp6       0      0  *.5900                 *.*                    LISTEN     
tcp4       0      0  *.548                  *.*                    LISTEN     
tcp6       0      0  *.548                  *.*                    LISTEN 

Again you can use the LISTEN etc. keywords to see the state of the connection. Yet another option is Knockknock. Knockknock is a python script that can be used to check which resources you have that may be malicious. Knockknock is available on github. You download and install it using git i.e.

$ git clone https://github.com/synack/knockknock.git

Then cd to the knockknock directry and execucute it. Here is how you find all running processes using knockknock.

$ sudo ./knockknock.py
<snipped>
[Chrome Browser Extensions]

Weebly - Website Builder 
 description: Easily create a free site, blog or online store. No technical skills required. Hosting included.
 id: cnocophcbjfiimmnhlhleaooedeheifb
 path: /Users/bob/Library/Application Support/Google/Chrome/Default/Extensions/cnocophcbjfiimmnhlhleaooedeheifb/1.0.5_0

None 
 description: None
 id: kfcfceejhleedfbabanmaamfiagjhncj


 path: None
<snipped>

The above shows the output from the knockknock python program. It can be used to see all browser extentions and processes that use your network connection. This way you can inspect any connections to see if there are any suspicious ones.

With fuser you can list all the open files on your OSX box. It'll give you the user and file handle for all open files. The output is really really cluttered in my opinion, so I'm sticking to the above alternatives.

July 06, 2016

Restoring OSX drives with photorec

To see what's left on your drive after using your machine for several years, recovering data using either testdisk or photorec can be quite fun. Install them on OSX using either brew or MacPorts, or Linux using your favourite  package manager.

$ sudo port install testdisk

The bove command installs both testdisk and photorec on osx. To run issue:

$ sudo photorec /log

Then follow the instructions in the command line. But before you run the recovery tool you'll have to find the drive you're attempting to recover from. This is done by using mount

$ mount
/dev/disk1 on / (hfs, local, journaled)
devfs on /dev (devfs, local, nobrowse)
localhost:/cZw0sWXSBvue3biH9HC_QR on /Volumes/MobileBackups (mtmfs, nosuid, read-only, nobrowse)
map -hosts on /net (autofs, nosuid, automounted, nobrowse)
map auto_home on /home (autofs, automounted, nobrowse)

The /dev/disk1 using the hfs filesystem is the one I'd like to recover stuff from, you can also use the diskutil program.

$ diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *500.1 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:          Apple_CoreStorage Macintosh HD            499.2 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk1 (internal, virtual):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           +498.9 GB   disk1
                                 Logical Volume on disk0s2
                                 71E61617-FD0B-4FFA-A432-4FE2737C217D


                                 Unlocked Encrypted

Again /dev/disk1 proves to be the one we're interested in. To recover it:

$ sudo photorec /log
Password:
PhotoRec 7.0, Data Recovery Utility, April 2015

Opens a new process in your terminal:

PhotoRec 7.0, Data Recovery Utility, April 2015
Christophe GRENIER <grenier@cgsecurity.org>
http://www.cgsecurity.org

Disk /dev/rdisk0 - 500 GB / 465 GiB (RO)
     Partition                  Start        End    Size in sectors
 2 P Unknown              409640   0  1 975503591   0  1  975093952 [Macintosh HD]


Pass 0 - Reading sector   74387313/975093952, 6/10 headers found
Elapsed time 0h08m58s - Estimated time to completion 1h48m34

Now all you have to do i wait. Photorec creates directories in the folder from which is is run. All these directories are named: recup_dir.* the asterix is replaced with numbers and the folders are filled with files.

$ ls -d ~/re*
/Users/bob/recup_dir.1335 /Users/bob/recup_dir.3194 /Users/bob/recup_dir.5052
/Users/bob/recup_dir.1336 /Users/bob/recup_dir.3195 /Users/bob/recup_dir.5053
/Users/bob/recup_dir.1337 /Users/bob/recup_dir.3196 /Users/bob/recup_dir.5054
/Users/bob/recup_dir.1338 /Users/bob/recup_dir.3197 /Users/bob/recup_dir.5055
<snipped>
/Users/bob/recup_dir.1339 /Users/bob/recup_dir.3198 /Users/bob/recup_dir.5056

Each directory is filled with files

$ ls /Users/bob/recup_dir.5052
f580969344.jpg   f580974912.pyc  f580980568.pyc  f580981752.pl    f580982432.gz    
<snipped>
f580987152.txt   f580987848.txt

Making it possible to use the normal file manipulation programs like: find, grep & sed. If you're looking for *.pdf file say anything with chess in it:

$ find ~/pdf/ -name '*.pdf' -exec pdfgrep -n 'chess' '{}' \;
525:    such as chess and checkers.
530:strategic game, such as checkers or chess. We will use, as an example, the much simpler
533:            For more complex games, such as checkers and chess, it is obviously infeasible to
533:      the position. For instance, in a chess program, the evaluation function measures such vari-
533:      mizing this function. The best computer chess programs have surprisingly sophisticated
533:            Nevertheless, for computer chess, the single most important factor seems to be number
533:      For instance, in a chess endgame, where there are relatively few pieces, the time savings
<snipped>

And there you have it. You can use '-H' in the pdfgrep part of the expression above to get a list of files instead of a linenumber, when you're examining many files like in the expression above this is a great help. If you have many files and directories you'll have to wait just like you did when running photorec. Have fun restoring.

July 01, 2016

The mad hat operator

Some stuff about the hat ^ operator. When I looked at this `buggar` I realised I hadn't used or seen this for many many years, which when you do a test of some sort is pretty bad. Because you look kinda lame when you get basic questions like this all wrong.

It's not that I hadn't used the xor operator I just never really took the time off to dwell into the operator since my time with boolean logic. So, I decided to work a little on the mad ^. In most languages like c/c++/java/python/bash and several others ^ means bitwise xor aka exclusive or. But in computer math ^ also means power of. The power off operator is the c function known as pow() (see man pow) for details.

Looking at a few simple examples: 10^9, 2^4, 5^7, 4^5, 3^3 and 5^0 seems to have simple answers.

10^9 = 1.000.000.000
2^4 = 128
5^7 = 78125
4^5 = 1024
3^3 = 27
5^0 = 1

All of the above are plain math answers. Answers that should be part of any ones math vocabulary. However, I'm not sure how the theory behind the 3^0 og 5^0 equations both equal 1 actually is. There's a theory on wikipedia that is really scientific and explains the details, there's a more fun description here however none of the articles say why x^0 is 1 they just claim it is? So why is it 1? Right about here I can hear all my previous teachers scream because it is.

I guess the answer to the above equations are: "It depends". Weather you mean in math or using binary operators. Since if you type the expression into either of the equations into any of the languages mentioned, you'll get completely different answers:

10^9 = 3
2^4 = 6
5^7 = 2
4^5 = 1
3^3 = 0
5^0 = 5

The cool statement of the math power off is that 2^1 or as it is stated x^1 = x. But in binary the answer is that it depends. Since: 2^1 = 3 and 3^1 = 2. Why is this, well the xor operator looks at the numbers binary and only lets values through that are not the same. Binary 2 and binary 1 are: 10 and 01 respectively. When you xor these you get:

10
01
--
11

11 is binary for 3. Xor 'promotes' or lets only ones through. Fine but why is 3^1 =2 then. Same simple answer:

11
01
--
10

Because any similar numbers is set to zero. So 0^0 = 0 and 1^1 = 0. Somehow I guess I have to accept the 2^0 math explanation since I seem to understand how the binary thing works. But anything times 0 is zero, execpt x^0. But can we prove it? Mathforum.org I guess we can:

x^0 = x^(1-1) = x^1 / x^1 = x/x = 1

Aka the law of the exponents. The important thing for me is that the reason I didn't know is because I never needed to know for dealing with pratical problems. But now I did find mathforum.org in the process guess I should read it now. Here's a small program madhat.c that plays with the ^ operator. Remember the answer, for this specific operator on how various equations turn out, is: "it depends".

June 28, 2016

Installing SIFT Forencics distribution in a virtualbox

How to get a glimpse of digital forencics. They have a few downloads for your vmware needs. Lets try their setup using virtualbox. Get the guides here. You'll need an ubuntu 14.04 image, the only image that can currently install SIFT. You can download your image here: Ubuntu alternate

$ wget http://releases.ubuntu.com/14.04/ubuntu-14.04.4-desktop-amd64.iso.torrent
$ rtorrent ~/Downloads/ubuntu-14.04.4-desktop-amd64.iso.torrent

Once your torrent download completes run the installation from virtualbox either using GUI or cmd line. Next you run installed the vboxlinuxadditions.sh to install the additions enabling you to copy and paste between the virtualbox and your host. Update the new box using the apt-get command.

$ sudo apt-get update
$ sudo apt-get upgrade

Then reboot and enable the shared clipboard between the virtualbox and your host machine, by selecting:

VirtualBox | Devices | Shared clipboard | Bi directional

Then paste the command below to install the SIFT workstation stuff. You should watch this SIFT distrobution video while you're installing. It'll give you a quick tour and examples of some of the commands that is part of the distribution i.e.
  • rip.pl - extract registry information like account creations etc.
  • deleted.pl - examine deleted registry entries
  • yaru - registry analyser that can show deleted keys etc. GUI app
  • pffexport - libpff extracting pst and ost data from MS outlook files
  • volatility - memory examinations 
  • bulk_extractor - dumps plenty data for analysis
  • sleuthkit - finding specific information in deleted inodes etc.

$ wget --quiet -O - https://raw.github.com/sans-dfir/sift- \ bootstrap/master/bootstrap.sh | sudo bash -s -- -i -s -y

[sudo] password for codemonkey: 
 * INFO: Welcome to the SIFT Bootstrap
 * INFO: This script will now proceed to configure your system.
 * INFO: You supplied the -y option, this script will not exit for any reason
 * INFO: OS: Ubuntu
 * INFO: Arch: 64
 * INFO: Version: 14.04
 * INFO: Updating your APT Repositories ... 
<snipped>
Installation Complete!

The documentation is always a work in progress, feel free to contribute!
Fork the sift-docs project and start sending your pull requests today.

Documentation: http://sift.readthedocs.org

The hostname was changed, you should relogin or reboot for it to take full effect.


sudo reboot

Wait until the SIFT distribution stuff has been installed then as you're instructed to to reboot.  A list of all the tools installed via the SIFT distribution can be found here. You should now be ready to start your first forencics using opensource tools. There's a getting started and introduction to version 2 book available freely here. Besides the information from the book the SANS blog have many great articles.

June 20, 2016

Git branching fswatch to test c compilation

Sometimes you'll need to work an a specific branch from a git repository to help people test their software. I tried to work and play a bit with the fswatch program. I had some issues and decided to assist a little with a specific issue. To work on git branches you'll need some help if you're not used to git, or if you don't remember the commands by heart. Here's discussion on branching. To get things going on the fswatch project clone the project and get your specific branch

$ git clone https://github.com/emcrisostomo/fswatch.git
$ git branch -v -a # listing all branches
* hotfix/1.9.3
  master
  remotes/origin/HEAD -> origin/master
  remotes/origin/develop
  remotes/origin/feature/improve-filters
  remotes/origin/gh-pages
  remotes/origin/hotfix/1.9.3
  remotes/origin/master
$ git checkout -b hotfix/1.9.3
$ git fetch origin
$ git pull origin hotfix/1.9.3
From https://github.com/emcrisostomo/fswatch
 * branch            hotfix/1.9.3 -> FETCH_HEAD
Updating a79113a..28b8998
Fast-forward
 libfswatch/NEWS                              | 4 ++++
 libfswatch/m4/libfswatch_version.m4          | 2 +-
 libfswatch/src/libfswatch/c/cevent.h         | 4 ++--
 libfswatch/src/libfswatch/c/cmonitor.h       | 2 +-
 libfswatch/src/libfswatch/c/libfswatch.h     | 1 +
 libfswatch/src/libfswatch/c/libfswatch_log.h | 2 +-
 m4/fswatch_version.m4                        | 2 +-

 7 files changed, 11 insertions(+), 6 deletions(-)

This gives you the branch 1.9.3 to check errors against. For fswatch you'll need to build and configure.


$ ./autogen.sh 
$ ./configure 
$ make
$ sudo make install

The above builds fswatch. To use the installed build with your test program, fire up gcc (not g++) as we're testing for plain c compatibility. Remember to point gcc and ld to the installed version of libfswatch.

$ gcc -I /usr/local/include -o "fswatch_test" fswatch_test.c /usr/local/lib/libfswatch.a 
In file included from fswatch_test.c:3:
In file included from /usr/local/include/libfswatch/c/libfswatch.h:33:
/usr/local/include/libfswatch/c/cevent.h:94:59: error: must use 'enum' tag to refer to type
      'fsw_event_flag'
  FSW_STATUS fsw_get_event_flag_by_name(const char *name, fsw_event_flag *flag);
                                                          ^
/usr/local/include/libfswatch/c/cevent.h:105:39: error: must use 'enum' tag to refer to type
      'fsw_event_flag'
  char *fsw_get_event_flag_name(const fsw_event_flag flag);
                                      ^
/usr/local/include/libfswatch/c/cevent.h:118:5: error: must use 'enum' tag to refer to type
      'fsw_event_flag'
    fsw_event_flag * flags;
    ^
In file included from fswatch_test.c:3:
In file included from /usr/local/include/libfswatch/c/libfswatch.h:35:
/usr/local/include/libfswatch/c/cfilter.h:47:5: error: must use 'enum' tag to refer to type
      'fsw_filter_type'
    fsw_filter_type type;
    ^
/usr/local/include/libfswatch/c/cfilter.h:57:5: error: must use 'enum' tag to refer to type
      'fsw_event_flag'
    fsw_event_flag flag;
    ^
In file included from fswatch_test.c:3:
/usr/local/include/libfswatch/c/libfswatch.h:102:37: error: must use 'enum' tag to refer to
      type 'fsw_monitor_type'
  FSW_HANDLE fsw_init_session(const fsw_monitor_type type = system_default_monitor_type);
                                    ^
/usr/local/include/libfswatch/c/libfswatch.h:102:59: error: C does not support default
      arguments
  FSW_HANDLE fsw_init_session(const fsw_monitor_type type = system_default_monitor_type);
                                                          ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 errors generated.

Sadly, there are still some errors in the plain C compilation of the branch. The last two are based on one of the main differences between plain C and C++. As C does not support default variable assignments to function parameters.

The other issues are based on the same issue, that the enum tag should be used (like the struct tag) when using enums as parameters. Which makes you wonder if the program was ever tested for C compatibility using gcc? most likely it was tested using g++ which compiles like a charm.

[Edit Wed 6 July 2016]

Fixing the issues in the C branch and linking with the correct lib will ensure the test program runs as expected, and prints the callback. To correctly build the test program all you need is:


$ gcc -I /usr/local/include -o "fswatch_test" fswatch_test.c /usr/local/lib/libfswatch.dylib

Notice the difference in the command above, we link against the *.dylib version instead of the static *.a lib. This will ensure that all the missing c++ symbols are found dynamically. Next, well test the program. Start a new shell and start the fswatch_test program. Then in a different terminal touch the file watched by the test program

In terminal 1
$ ./fswatch_test

In terminal 2
$ touch ~/Documents/src/bash/map_network_drives.sh 
$ touch ~/Documents/src/bash/map_network_drives.sh 

In terminal 1
my_callback: 1
my_callback: 1
^C

The program seems to be running, the callback is called at every touch. Now we need to create a patch against the origin to get the fixes into the main branch. But first lets the the changes into the hotfix branch.

$ git add libfswatch/src/libfswatch/c/cevent.h 
$ git add libfswatch/src/libfswatch/c/cfilter.h 
$ git add libfswatch/src/libfswatch/c/libfswatch.h 
$ git commit -m "Fixed headers for c compilation"
[hotfix/1.9.3 f9222ae] Fixed headers for c compilation
 3 files changed, 6 insertions(+), 6 deletions(-)
$ git push --set-upstream origin hotfix/1.9.3

There, the changes should now be in the hotfix branch safely tugged away. This means we can create a pull request, done by clicking the 'compare & pull request" button in the branch. Then all we have to do is wait. The patching thing is handled by git so we won't have to do it the diff way. Changes to the fswatch_test.c are pushed to the playground repository.



June 16, 2016

Filesystem events

In 2011 I started working on a project that at its core used the Linux iNotify kernel service to distribute and record file events. These events where then processed to a subscription service allowing subscribing clients to respond to these file changes. Today, in 2016 I considered porting parts of this system for producing an API, when I stumbled upon the magnificent fswatch program written and maintained by Enrico M. Crisostomo.

The fswatch will save me plenty of work in the porting task. As fswatch the core services of inotify, FSEvents, Kqueue, ReadDirectoryW etc. The following post is an investigation of the fswatch program, with respect to the requirements for the earlier mentioned project.  Fswatch comes from a couple of installation sources, MacPorts, Brew and source, choose your favourite method to get fswatch.

$ sudo port install fswatch
Password:
Warning: port definitions are more than two weeks old, consider updating them by running 'port selfupdate'.
--->  Fetching archive for fswatch
--->  Attempting to fetch fswatch-1.9.2_0.darwin_15.x86_64.tbz2 from https://packages.macports.org/fswatch
--->  Attempting to fetch fswatch-1.9.2_0.darwin_15.x86_64.tbz2.rmd160 from https://packages.macports.org/fswatch
--->  Installing fswatch @1.9.2_0
--->  Activating fswatch @1.9.2_0
--->  Cleaning fswatch
--->  Updating database of binaries
--->  Scanning binaries for linking errors

--->  No broken files found.       

That takes care of the fswatch installation. If you're curious like me you'll need the source to check any details. The source for fswatch can be downloaded from the official github repository.

$ tar -zxvf fswatch-1.9.2.tar.gz 
x fswatch-1.9.2/
x fswatch-1.9.2/.gitignore
x fswatch-1.9.2/.idea/
<snipped>

The above extracts the source. Now we're ready for investigations. According to the fswatch readme file you start fswatch by giving the program a set of paths to monitor i.e. 

$ fswatch [options] ... path-0 ... path-n
So, lets check to see what happens when we run the program fire up a terminal and issue:

$ fswatch -1 /Users/bobo/Documents/src/bash/

The -1 opting to fswatch ensures a single event is thrown. Notice the process waits until something happens. Now fire up another terminal and create a new file in the watched directory like this:

$ touch /Users/bobo/Documents/src/newfile.sh

As soon as the new file is created fswatch exits after displaying the new files path. You can see this when you switch back to the first terminal, the terminal containing the fswatch program.

/Users/bobo/Documents/src/bash/newfile.sh

Now, lets try to leave fswatch running for a while, to see how it reports on several changes.

$ fswatch /Users/bobo/Documents/src/bash/

Try to remove the newly created file, in a different terminal than the one running fswatch issue

$ echo "stuff" > /Users/bobo/Documents/src/newfile.sh

Notice that fswatch again reports the path but doesn't stop running.

$ echo "more stuff" >> /Users/bobo/Documents/src/bash/newfile.sh

Another echo of the affected file, lets check the changes to the file:

$ cat /Users/bobo/Documents/src/bash/newfile.sh 
stuff
more stuff

Both lines are in there. Now lets's remove the file 

$ rm /Users/bobo/Documents/src/bash/newfile.sh

And again fswatch reports the filepath, but now the file is removed. Notice that the default behaviour of fswatch is to only report the path of the affected file, not what happened to the file. We'd like to know the change as this is part of the provided subscription service. Also notice that when catting the file fswatch did not report an event, this is because the file was not written only accessed. This means that fswatch cannot report file access only writes. Let's check to see if this holds.

$ fswatch -nt /Users/bobo/Documents/src/bash/

The above command starts fswatch using two options, the first (n) displays the fswatch event number, the second (t) prints a timestamp. Now from a different terminal run the above commands again.

$ touch /Users/bobo/Documents/src/bash/newfile.sh
$ echo "stuff" > /Users/bobo/Documents/src/bash/newfile.sh 
$ echo "more stuff" >> /Users/bobo/Documents/src/bash/newfile.sh 
$ rm /Users/bobo/Documents/src/bash/newfile.sh 

Then check the report from fswatch in the previous terminal:

Wed Jun 15 10:51:30 2016 /Users/bobo/Documents/src/bash/newfile.sh 514
Wed Jun 15 10:51:43 2016 /Users/bobo/Documents/src/bash/newfile.sh 517
Wed Jun 15 10:51:50 2016 /Users/bobo/Documents/src/bash/newfile.sh 517
Wed Jun 15 10:51:53 2016 /Users/bobo/Documents/src/bash/newfile.sh 525

So, as you can see fswatch reports different events when you use the -n option, and a timestamp thanks to the -t option. The events are somewhat cloudy, since the numbers 514,517 and 525 really dosen't tell much about what happened. We only know because the issued the commands from the terminal that forced these events. Lets try the -x option to see if this contain more information.

$ fswatch -xt /Users/bobo/Documents/src/bash/

If you issue fswatch -xtn the output for the -x option is suppressed due to the -n option. Again issue the commands and check the output.

Wed Jun 15 11:01:40 2016 /Users/bobo/Documents/src/bash/newfile.sh Created IsFile
Wed Jun 15 11:01:50 2016 /Users/bobo/Documents/src/bash/newfile.sh Created PlatformSpecific Updated IsFile
Wed Jun 15 11:01:53 2016 /Users/bobo/Documents/src/bash/newfile.sh Created PlatformSpecific Updated IsFile
Wed Jun 15 11:02:01 2016 /Users/bobo/Documents/src/bash/newfile.sh Created Removed PlatformSpecific Updated IsFile

Notice the:

Created IsFile
Created PlatformSpecific Updated IsFile
Created Removed PlatformSpecific Updated IsFile

All events are nicely shown in text, ready to be used with any other system. There are many more options to the fswatch program. These options can be used in many different ways. You should check the documentation for details. For me these details are sufficient for the subscription requirements needed for an update service.

There is only one issue with fswatch. I'd love if the program could be run i a deamon mode, allowing me to add and remove watches on the fly. This is the only issues that needs solving before the subscriber update relations can be ported.

Luckily,  The fswatch source includes the libfswatch, which can be used from within any c/cpp source project. The library is sufficient for including fswatch in a subscription deamon. Libfswatch is installed along side the fswatch program. If you used MacPorts the library recides in the /opt/local/lib/ folder, and it's include files are present in the /opt/local/include/libfswatch folder.

Optional you can build the libraries by hand using the source. To build and install libfswatch simply cd to the root of the fswatch source code and run the following commands: ./autogen.sh, ./configure, make, and make install.

$fswatch-1.9.2/$ ./autogen.sh 
autoreconf: Entering directory `.'
autoreconf: running: /opt/local/bin/autopoint --force
Copying file ABOUT-NLS
Creating directory config
Copying file config/config.rpath
Copying file m4/codeset.m4
Copying file m4/extern-inline.m4
Copying file m4/fcntl-o.m4
Copying file m4/gettext.m4
Copying file m4/glibc2.m4
<snipped>
configure.ac:44: installing 'config/install-sh'
configure.ac:44: installing 'config/missing'
doc/Makefile.am:16: installing 'config/mdate-sh'
doc/Makefile.am:16: installing 'config/texinfo.tex'
src/Makefile.am: installing 'config/depcomp'
autoreconf: Leaving directory `.'

Now, we're interested in building only the libfswatch therefore we need to change to this directory and run configure from there

fswatch-1.9.2/$ cd libfswatch/
fswatch-1.9.2/libfswatch/$ ./configure
checking build system type... x86_64-apple-darwin15.5.0
checking host system type... x86_64-apple-darwin15.5.0
checking target system type... x86_64-apple-darwin15.5.0
checking for a BSD-compatible install... /opt/local/bin/ginstall -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /opt/local/bin/gmkdir -p
<snipped>
config.status: executing po-directories commands
config.status: creating po/POTFILES
config.status: creating po/Makefile

When configure is done, run make

fswatch-1.9.2/libfswatch/$ make
/Applications/Xcode.app/Contents/Developer/usr/bin/make  all-recursive
Making all in src
Making all in libfswatch
  CXX      c/cevent.lo
  CXX      c/libfswatch.lo
  CXX      c/libfswatch_log.lo
  CXX      c++/libfswatch_exception.lo
  CXX      c++/event.lo
<snipped>
make[3]: Nothing to be done for `all-am'.
make[2]: Nothing to be done for `all-am'.

The the lib is installed issuing make install

fswatch-1.9.2/libfswatch/$ make install
Making install in src
Making install in libfswatch
 /opt/local/bin/gmkdir -p '/usr/local/lib'
<snipped>
/opt/local/bin/gmkdir -p '/usr/local/share/doc/libfswatch'
 /opt/local/bin/ginstall -c -m 644 README.md AUTHORS COPYING LICENSE NEWS '/usr/local/share/doc/libfswatch'

Lets check to see if we can find the installed libfswatch

$ ls -l /usr/local/lib/libfswatch.*
-rwxr-xr-x  1 bobo  wheel   184044 15 Jun 11:45 /usr/local/lib/libfswatch.6.dylib
-rw-r--r--  1 bobo  wheel  4431560 15 Jun 11:45 /usr/local/lib/libfswatch.a
lrwxr-xr-x  1 bobo  wheel       18 15 Jun 11:45 /usr/local/lib/libfswatch.dylib -> libfswatch.6.dylib
-rwxr-xr-x  1 bobo  wheel      965 15 Jun 11:45 /usr/local/lib/libfswatch.la

It's present in the /usr/local/lib/ directory on my machine, hopefully its also there on yours. Next check the libfswatch API headers

$ ls -lR /usr/local/include/libfswatch/
total 0
drwxr-xr-x   9 bobo  admin  306 15 Jun 11:45 c
drwxr-xr-x  13 bobo  admin  442 15 Jun 11:45 c++

/usr/local/include/libfswatch//c:
total 72
-rw-r--r--  1 bobo  admin  5688 15 Jun 11:45 cevent.h
-rw-r--r--  1 bobo  admin  1522 15 Jun 11:45 cfilter.h
-rw-r--r--  1 bobo  admin  1847 15 Jun 11:45 cmonitor.h
-rw-r--r--  1 bobo  admin  2684 15 Jun 11:45 error.h
-rw-r--r--  1 bobo  admin  6991 15 Jun 11:45 libfswatch.h
-rw-r--r--  1 bobo  admin  2900 15 Jun 11:45 libfswatch_log.h
-rw-r--r--  1 bobo  admin  1456 15 Jun 11:45 libfswatch_types.h

/usr/local/include/libfswatch//c++:
total 136
-rw-r--r--  1 bobo  admin   3312 15 Jun 11:45 event.hpp
-rw-r--r--  1 bobo  admin   2776 15 Jun 11:45 filter.hpp
-rw-r--r--  1 bobo  admin   2559 15 Jun 11:45 fsevents_monitor.hpp
-rw-r--r--  1 bobo  admin   2854 15 Jun 11:45 kqueue_monitor.hpp
-rw-r--r--  1 bobo  admin   2132 15 Jun 11:45 libfswatch_exception.hpp
-rw-r--r--  1 bobo  admin   1838 15 Jun 11:45 libfswatch_map.hpp
-rw-r--r--  1 bobo  admin   1767 15 Jun 11:45 libfswatch_set.hpp
-rw-r--r--  1 bobo  admin  27727 15 Jun 11:45 monitor.hpp
-rw-r--r--  1 bobo  admin   2625 15 Jun 11:45 path_utils.hpp
-rw-r--r--  1 bobo  admin   2807 15 Jun 11:45 poll_monitor.hpp
-rw-r--r--  1 bobo  admin   1693 15 Jun 11:45 string_utils.hpp

All there and ready to run. There may be an issue with g++ and the use of the libraries as there is no *.pc file that explains the system where the libraries are. If you just installed fswatch, you can use the installed library directly.

The source for the simple fswatch_test program has been taken from the readme file of fswatch, I only added some error checking etc.

$ cat fswatch_test.c
#include <stdio.h>
#include <stdlib.h>
#include <libfswatch/c/libfswatch.h>

/**
 * The following function implements the callback functionality
 * for testing eventnumber send from the
 * libfswatch library. See FSW_CEVENT_CALLBACK for details.
 */
void my_callback(fsw_cevent const * const events, const unsigned int event_num, void * data) {
  printf("my_callback: %d\n",event_num);
}

int main(int argc, char **argv) {

  if(2 <= argc) {
    if(FSW_OK == fsw_init_library()) {

      const FSW_HANDLE handle = fsw_init_session();
      if(FSW_INVALID_HANDLE != handle) {

/* Loop and add all paths passed to the fsw session */
int j = 0;
for(j=1;j<argc;j++) {
 if(FSW_OK != fsw_add_path(handle,argv[j])) {
   fsw_last_error();
 }
}

if(FSW_OK != fsw_set_callback(handle, my_callback,NULL)) {
 fsw_last_error();
}

if(FSW_OK != fsw_start_monitor(handle)) {
 fsw_last_error();  
}
      }
      else {
fsw_last_error();
(void)printf("Invalid fswatch handle: %d\n",handle);
      }
    }
    else {
      fsw_last_error();  
      (void)printf("fswatch cannot be initialised!\n");
    }
  }
  else {
    (void)printf("usage: %s [path]\n",argv[0]);
  }
  return 0;

}

The above program can be used like the fswatch program. As the test program simply adds all passed paths using the libfswatch calls. Then the program registers a callback for echoing the fs events to the terminal. Finally, the test program starts the fsw monitor. All this is done using the libfswatch c style functions. If something goes wrong the test program checks the error and writes this to the terminal. Compile and run using:

$ g++ -I /opt/local/include -o "fswatch_test" fswatch_test.c /opt/local/lib/libfswatch.a 

Or you can add the LDFLAGS environment variable to your path or to a seperate file and then source this file when building. This issue is explained in details here. Now, run the program


$ ./fswatch_test /Users/bobo/Documents/src/bash/

Segmentation fault: 11

Yikes, segfault. Seems like we have some debugging on the road ahead. But before I dwell into gdb to check whats wrong, I usually run the program using valgrind, usually this neat little program will tell me where things went wrong.

$ valgrind ./fswatch_test "/Users/bobo/Documents/src/bash/"
==1021== Memcheck, a memory error detector
==1021== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1021== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==1021== Command: ./fswatch_test /Users/bobo/Documents/src/bash/
==1021== 
--1021-- run: /usr/bin/dsymutil "./fswatch_test"
warning: no debug symbols in executable (-arch x86_64)
==1021== Invalid write of size 1
==1021==    at 0x100005BB2: fsw::monitor::set_allow_overflow(bool) (in ./fswatch_test)
==1021==    by 0x100001DE8: fsw_start_monitor (in ./fswatch_test)
==1021==    by 0x10000129A: main (in ./fswatch_test)
==1021==  Address 0x51 is not stack'd, malloc'd or (recently) free'd
==1021== 
==1021== 
==1021== Process terminating with default action of signal 11 (SIGSEGV)
==1021==  Access not within mapped region at address 0x51
==1021==    at 0x100005BB2: fsw::monitor::set_allow_overflow(bool) (in ./fswatch_test)
==1021==    by 0x100001DE8: fsw_start_monitor (in ./fswatch_test)
==1021==    by 0x10000129A: main (in ./fswatch_test)
==1021==  If you believe this happened as a result of a stack
==1021==  overflow in your program's main thread (unlikely but
==1021==  possible), you can try to increase the size of the
==1021==  main thread stack using the --main-stacksize= flag.
==1021==  The main thread stack size used in this run was 8388608.
==1021== 
==1021== HEAP SUMMARY:
==1021==     in use at exit: 22,441 bytes in 196 blocks
==1021==   total heap usage: 268 allocs, 72 frees, 28,585 bytes allocated
==1021== 
==1021== LEAK SUMMARY:
==1021==    definitely lost: 40 bytes in 2 blocks
==1021==    indirectly lost: 0 bytes in 0 blocks
==1021==      possibly lost: 0 bytes in 0 blocks
==1021==    still reachable: 392 bytes in 8 blocks
==1021==         suppressed: 22,009 bytes in 186 blocks
==1021== Rerun with --leak-check=full to see details of leaked memory
==1021== 
==1021== For counts of detected and suppressed errors, rerun with: -v
==1021== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault: 11

Luckily, segfault again ;) Now valgrinds output points to the error causing the segfault. Examining the valgrind output we can see the callstack, equivalent to gdb backtrace functionality, points to the function: fsw::monitor::set_allow_overflow(bool). This function is part of the fswatch library, luckily we have the source. The error also states that the address 0x51 is not stack, or allocated. Infact an address of 0x51 seems to be uninitialized memory.

$ grep -n "fsw_start_monitor" ../fswatch-1.9.2/libfswatch/src/libfswatch/c/*.cpp
../fswatch-1.9.2/libfswatch/src/libfswatch/c/libfswatch.cpp:212: * next time a monitor is started with fsw_start_monitor().
../fswatch-1.9.2/libfswatch/src/libfswatch/c/libfswatch.cpp:273: *     fsw_start_monitor(handle);
../fswatch-1.9.2/libfswatch/src/libfswatch/c/libfswatch.cpp:840:FSW_STATUS fsw_start_monitor(const FSW_HANDLE handle)

Finds a few things, like the definition of the fsw_start_monitor function on line 840. Lets sed it

$ sed -n '840,880'p ../fswatch-1.9.2/libfswatch/src/libfswatch/c/libfswatch.cpp 
FSW_STATUS fsw_start_monitor(const FSW_HANDLE handle)
{
  try
  {
#ifdef HAVE_CXX_MUTEX
    unique_lock<mutex> session_lock(session_mutex, defer_lock);
    session_lock.lock();
#endif

    FSW_SESSION *session = get_session(handle);

#ifdef HAVE_CXX_MUTEX
# ifdef HAVE_CXX_ATOMIC
    if (session->running.load(memory_order_acquire))
      return fsw_set_last_error(int(FSW_ERR_MONITOR_ALREADY_RUNNING));
# endif

#  ifdef HAVE_CXX_UNIQUE_PTR
    unique_ptr<mutex>& sm = session_mutexes.at(handle);
    lock_guard<mutex> lock_sm(*sm.get());
#  else
    mutex * sm = session_mutexes.at(handle);
    lock_guard<mutex> lock_sm(*sm);
#  endif

    session_lock.unlock();
#endif

    if (!session->monitor)
      create_monitor(handle, session->type);

    session->monitor->set_allow_overflow(session->allow_overflow);
    session->monitor->set_filters(session->filters);
    session->monitor->set_event_type_filters(session->event_type_filters);
    session->monitor->set_follow_symlinks(session->follow_symlinks);
    if (session->latency) session->monitor->set_latency(session->latency);
    session->monitor->set_recursive(session->recursive);
    session->monitor->set_directory_only(session->directory_only);

#ifdef HAVE_CXX_MUTEX
# ifdef HAVE_CXX_ATOMIC

The bold line seems to be the call in question, according to valgrinds output. Lets have a look in the monitor implementation file.

$ sed -n '73,83'p ../fswatch-1.9.2/libfswatch/src/libfswatch/c++/monitor.cpp
  void monitor::set_allow_overflow(bool overflow)
  {
    allow_overflow = overflow;
  }

This small function seems to set a member in the monitor object. So, by reading the valgrind error again I'd think that the problem is that the session or monitor object has not been correctly allocated or stacked. So the issue must be that either the c interface I used in my test program does not ensure that the session or monitor is not created correctly, or the session or monitor class has some kind of initialisation error. That's as far as the evaluation goes for now. Stranded on an initialisation issue.