Permalink
Browse files

original 0.2 code from jeffm's tarfile

  • Loading branch information...
0 parents commit 15d86eafb807756ee50e0dda8f2284a302ee7de8 @massemanet committed Jan 30, 2010
Showing with 1,074 additions and 0 deletions.
  1. +91 −0 README
  2. +24 −0 src/Makefile
  3. +81 −0 src/erl_comm.c
  4. +9 −0 src/erl_comm.h
  5. +174 −0 src/inotify.erl
  6. +127 −0 src/inotify_driver.c
  7. +476 −0 src/inotify_erlang.c
  8. +28 −0 src/inotify_erlang.h
  9. +49 −0 src/iwatch.erl
  10. +15 −0 src/test.erl
@@ -0,0 +1,91 @@
+
+Introduction
+------------
+Inofity is on erlang port for the Linux inotify API allowing one to monitor
+changes to files and directory in the filesystem.
+
+Installation
+------------
+1. Change to the src directory
+2. Execute make
+3. Execute make test. When you run make test it runs inotify:test() and you
+should see output similar to
+
+ $ make test
+ erl -noshell -eval "inotify:test(), erlang:halt()."
+ Simplistic test/example
+ Start... Open the port and receive a file descriptor... F = 3
+ list..L = [3]
+ Watch for any changes in a Directory... W = 1
+ launch listener....
+ start playing with the file...
+ attempt to create file "../test/file"
+ listener got: {event,1,[create],0,[102,105,108,101,0,0,0,0,0,0,0,0,0,0,0,0]}
+ listener got: {event,1,[create],0,[102,105,108,101,0,0,0,0,0,0,0,0,0,0,0,0]}
+ write a message to file
+ listener got: {event,1,[modify],0,[102,105,108,101,0,0,0,0,0,0,0,0,0,0,0,0]}
+ close the file
+ listener got: {event,1,
+ [close_write],
+ 0,
+ [102,105,108,101,0,0,0,0,0,0,0,0,0,0,0,0]}
+ delete file "../test/file"
+ listener got: {event,1,[delete],0,[102,105,108,101,0,0,0,0,0,0,0,0,0,0,0,0]}
+ end playing with file
+ stop the listener...
+ stop inotify controller...
+ test is now concluded
+
+The test creates a file in the test directory, writes to it and then deletes it.
+
+4. Before installing alter the line
+
+ -define(PORTPROG, "./inotify").
+
+ in inotify.erl to reflect where the program will be installed.
+
+5. re-run make
+
+6. copy the copiled files to the desired location
+
+
+Using Intoify
+-------------
+For an example on how to use inotify take a look at the function test/0 in
+intoify.erl.
+
+The listener process gets a message of the form
+ {event, WatchDescriptor, EventList, Cookie, Name}
+where
+ WatchDescriptor is the watch descriptor which caused the event
+ EventList is one or more event which is/are the reason for the message these
+ include,
+ access, attrib, close_write, close_nowrite, create, delete, delete_self,
+ modify, move_self, moved_from, moved_to, open
+ Cookie
+ Name is the filesystem name relative to the base which caused the event. It
+ the example test above the list [102,105,108,101,0,0,0,0,0,0,0,0,0,0,0,0]
+ is the string "file" zero padded which is the file referred to by the
+ event relative to the test directory with the attached inotify watch.
+
+
+License
+-------
+In short, you can do anything you want with the code including using it as part
+of you plan for world domination (if your successful can I have one of the nicer
+countries please). No responsiblity it taken for the fitness of the any purpose,
+etc, etc. The only thing I ask is that if you find a bug and fix send me the
+patch. Likewise, feature suggestions and patches are welcome.
+
+TODO
+----
+* Write a makefile that works
+* add support for multiple controller functions
+* Write some documentation!
+
+
+
+Release History
+---------------
+20090221 release 0.2 bug fix
+20080929 initial release version 0.1
@@ -0,0 +1,24 @@
+.SUFFIXES: .erl .beam .yrl
+
+LIBS= -l ei
+
+CFLAGS= -Wall
+
+.erl.beam:
+ erlc -W $<
+
+.c.o:
+ gcc -o $<
+
+MODS = inotify
+
+all: ${MODS:%=%.beam} inotify
+
+inotify: inotify_driver.c erl_comm.c inotify_erlang.c
+ gcc -o inotify erl_comm.c inotify_driver.c inotify_erlang.c $(LIBS) $(CFLAGS)
+
+test:
+ erl -noshell -eval "inotify:test(), erlang:halt()."
+
+clean:
+ rm inotify *.beam
@@ -0,0 +1,81 @@
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <ei.h>
+#include "erl_comm.h"
+
+/*
+ * inputs
+ * buf a pointer the buffer to place the message in
+ * size size of buf
+ * curpos current offset into buf. Should be set to 0 before
+ * initial call
+ *
+ * returns < 0 on read() error
+ * == 0 on read() returning 0
+ * 1 when there is more to read
+ * 2 when the message is complete
+ */
+int read_cmd(char *buf, int *size, int *curpos)
+{
+ int len;
+ int count;
+ int desired;
+
+ if (*curpos < 2) {
+ /* read header */
+ count = read(0, buf + *curpos, 2 - *curpos);
+
+ if (count <= 0)
+ return(count); /* Error or fd is closed */
+
+ *curpos += count;
+ if (*curpos < 2)
+ return(1);
+ }
+ /* calculate the total message length and
+ * the desired amount to read taking into account
+ * the ammount already read
+ */
+ len = (buf[0] << 8) | buf[1];
+ desired = len - *curpos + 2;
+
+ /* check buffer size and realloc if necessary */
+ if (len > *size) {
+ buf = (char *) realloc(buf, len);
+ if (buf == NULL)
+ return -1;
+ *size = len;
+ }
+
+ /* read message body */
+ count = read(0, buf + *curpos, desired);
+ if (count <= 0)
+ return(0);
+
+ *curpos += count;
+ return(2);
+}
+
+int write_cmd(ei_x_buff *buff) {
+ char li;
+
+ li = (buff->index >> 8) & 0xff;
+ write_exact(&li, 1);
+ li = buff->index & 0xff;
+ write_exact(&li, 1);
+
+ return write_exact(buff->buff, buff->index);
+}
+
+int write_exact(char *buf, int len) {
+ int i, wrote = 0;
+
+ do {
+ if ((i = write(1, buf+wrote, len-wrote)) <= 0)
+ return(i);
+ wrote += i;
+ } while (wrote<len);
+
+ return(len);
+}
@@ -0,0 +1,9 @@
+#ifndef ERL_COMM_H
+#define ERL_COMM_H
+
+int read_cmd(char *buf, int *size, int *curpos);
+int read_exact(char *buf, int len);
+int write_cmd(ei_x_buff *buff);
+int write_exact(char *buf, int len);
+
+#endif
@@ -0,0 +1,174 @@
+-module(inotify).
+-export([start/1, stop/0]).
+-export([test_start/0, test_end/2, test/0]).
+-export([open/0, controlling_process/1, add/3, remove/2, list/0, close/1]).
+
+-define(PORTPROG, "./inotify").
+
+start(Controller) when is_pid(Controller) ->
+ spawn(fun() ->
+ register(?MODULE, self()),
+ process_flag(trap_exit, true),
+ Port = open_port({spawn, ?PORTPROG},
+ [{packet, 2}, binary, exit_status]),
+ loop(Port, Controller)
+ %% The following is only used for development and testing
+ %% try loop(Port, Controller)
+ %% catch
+ %% T:Err ->
+ %% error_logger:error_msg("catch: ~p:~p~n", [T, Err])
+ %% end
+ end).
+
+stop() ->
+ ?MODULE ! stop.
+
+
+test_start() ->
+ io:format("open~n"),
+ {ok, F} = open(),
+ io:format("list~n"),
+ {ok, L} = list(),
+ io:format("List returned ~p~n", [L]),
+ {ok, W} = add(F, "file", all),
+ {ok, F, W}.
+
+test_end(F,W) ->
+ io:format("remove~n"),
+ ok = remove(F,W),
+ io:format("close~n"),
+ ok = close(F).
+
+test() ->
+ %% this is the test file
+ Dir = "../test",
+ File = "file",
+ io:format("Simplistic test/example~n"),
+ io:format("Start... "),
+ start(self()),
+ io:format("Open the port and receive a file descriptor... "),
+ {ok, F} = open(),
+ io:format("F = ~p~n", [F]),
+ io:format("list.."),
+ {ok, L} = list(),
+ io:format("L = ~p~n", [L]),
+ io:format("Watch for any changes in a Directory... "),
+ {ok, W} = add(F, Dir, all),
+ io:format("W = ~p~n", [W]),
+ io:format("launch listener....~n"),
+ Listener = spawn(fun() -> test_listener() end),
+ controlling_process(Listener),
+ io:format("start playing with the file...~n"),
+ test_player(Dir ++ "/" ++ File),
+ io:format("stop the listener...~n"),
+ Listener ! stop,
+ io:format("stop inotify controller...~n"),
+ stop(),
+ io:format("test is now concluded~n").
+
+test_player(File) ->
+ io:format("attempt to create file ~p~n", [File]),
+ {ok, F} = file:open(File, write),
+ timer:sleep(1000),
+ io:format("write a message to file~n"),
+ file:write(F, "a line of text~n"),
+ timer:sleep(1000),
+ io:format("close the file~n"),
+ file:close(F),
+ timer:sleep(1000),
+ io:format("delete file ~p~n", [File]),
+ file:delete(File),
+ timer:sleep(1000),
+ io:format("end playing with file~n").
+
+test_listener() ->
+ receive
+ stop ->
+ ok;
+ Msg ->
+ io:format("listener got: ~p~n", [Msg]),
+ test_listener()
+ end.
+
+%%
+%% open() -> {ok, Fd} | {error, Reason}
+%%
+open() ->
+ call_port({open}).
+%%
+%% add(Fd, Pathname, EventList) -> {ok, Wd} | {error, Reason}
+%%
+add(Fd, Pathname, EventList) when is_integer(Fd), is_list(Pathname),
+ (is_atom(EventList) orelse is_list(EventList)) ->
+ call_port({add, Fd, Pathname, EventList}).
+
+%%
+%% controlling_process(Pid) -> ok
+%%
+controlling_process(Pid) ->
+ ?MODULE ! {controlling_process, self(), Pid},
+ receive
+ {?MODULE, Result} ->
+ Result;
+ Other ->
+ Other
+ end.
+
+%%
+%% remove(Fd, Wd) -> ok| {error, Reason}
+%%
+remove(Fd, Wd) when is_integer(Fd), is_integer(Wd) ->
+ call_port({remove, Fd, Wd}).
+%%
+%% list() -> [Fds]
+%%
+list() ->
+ call_port({list}).
+%%
+%% close(Fd) -> ok | {error, Reason}
+%%
+close(Fd) when is_integer(Fd) ->
+ call_port({close, Fd}).
+
+
+call_port(Msg) ->
+ ?MODULE ! {call, self(), Msg},
+ receive
+ {?MODULE, Result} ->
+ Result;
+ Other ->
+ Other
+ end.
+
+loop(Port, Controller) ->
+ receive
+ {call, Caller, Msg} ->
+ erlang:port_command(Port, term_to_binary(Msg)),
+ receive
+ {Port, {data, Data}} ->
+ Caller ! binary_to_term(Data);
+ {Port, {exit_status, Status}} when Status > 128 ->
+ exit({port_terminated, Status});
+ {Port, {exit_status, Status}} ->
+ exit({port_terminated, Status});
+ {'EXIT', Port, Reason} ->
+ exit(Reason)
+ %% following two lines used for development and testing only
+ %% Other ->
+ %% io:format("received: ~p~n", [Other])
+ end,
+ loop(Port, Controller);
+ {Port, {data, Msg}} ->
+ Controller ! binary_to_term(Msg),
+ loop(Port, Controller);
+ {controlling_process, Caller, Pid} ->
+ Caller ! ok,
+ loop(Port, Pid);
+ stop ->
+ erlang:port_close(Port),
+ exit(normal);
+ {'EXIT', Port, Reason} ->
+ exit({port_terminated, Reason});
+ _Other ->
+ loop(Port, Controller)
+ end.
Oops, something went wrong.

0 comments on commit 15d86ea

Please sign in to comment.