Unbreak comon and other scripts that use a '-'
[vsys.git] / backend.ml
1 (** backend.ml: 
2   Defines handlers for events related to the backend directory, where the
3        scripts are stored. Eg. a new script results in a new part of pipes in
4        the frontend etc. These handlers are defined in the backendHandler
5        class.
6
7   @author Sapan Bhatia <sapanb\@cs.princeton.edu>
8   *)
9
10 open Unix
11 open Globals
12 open Dirwatcher
13 open Inotify
14 open Fifowatcher
15 open Frontend
16 open Printf
17
18 (** Turn an absolute path into a relative path. *)
19 let delete_prefix prefix str =
20   let len = String.length str in
21   let plen = String.length prefix in
22     if (String.sub str 0 plen <> prefix) 
23     then 
24       (* XXX can a user make this happen? *)
25       raise Bad_path
26     else
27         Relpath(String.sub str (plen+1) (len-plen-1))
28
29 let rec list_check lst elt =
30   match lst with
31     | [] -> false
32     | car::cdr -> if (car==elt) then true else list_check cdr elt
33
34 (** The backendhandler class: defines event handlers for events in
35 the backend backend directory.
36   @param dir_root The location of the backend in the server context (eg. root context for vservers)
37   @param frontend_list List of frontends to serve with this backend
38   *)
39 class backendHandler dir_root (frontend_lst: frontendHandler list) =
40    let mk_rel_path = delete_prefix dir_root in object(this)
41
42      (** Regular expression that defines a legal script name. Filter out
43        * temporary files using it *)
44      val file_regexp = Str.regexp "^[a-zA-Z][a-zA-Z0-9_\.\-]*$"
45      val acl_file_regexp = Str.regexp ".*acl$"
46
47      (** Somebody created a new directory *)
48      method private new_dir slice_list fqp func =
49        let s = Unix.stat fqp in
50          List.iter 
51            (fun frontend->
52               try begin 
53                 frontend#mkdir (mk_rel_path fqp) (s.st_perm);
54                 Dirwatcher.add_watch fqp [S_Create;S_Delete] func 
55               end
56               with _ ->
57                 logprint "Could not create %s. Looks like a slice shot itself in the foot\n" fqp;
58            )
59            slice_list
60
61      (** Somebody copied in a new script *)
62      method private new_script slice_list fqp =
63        let s = Unix.stat fqp in
64          List.iter (fun frontend->
65                       frontend#mkentry (mk_rel_path fqp) fqp (s.st_perm)) 
66                    slice_list 
67
68      method private make_filter acl_fqp =
69        let filter = Hashtbl.create 16 in
70        try 
71          let acl_file = open_in acl_fqp in
72          let rec read_acl cur_filter = 
73            let next_item = 
74              try Some(input_line acl_file)
75              with _ -> None
76            in
77              match next_item with
78                | None -> close_in acl_file;cur_filter
79                | Some(item) -> 
80                    Hashtbl.add cur_filter item true;
81                    read_acl cur_filter
82          in
83            Some(read_acl filter)
84        with _ ->
85          None
86
87      method is_acl fname = Str.string_match acl_file_regexp fname 0
88
89      (** Gets called every time there's an inotify event at the backend 
90        @param dirname Name of the backend directory
91        @param evlist Description of what happened
92        @param fname Name of the file that the event applies to
93      *)
94      method handle_dir_event _ dirname evlist fname = 
95        let fqp = String.concat "/" [dirname;fname] in
96          if ((Str.string_match file_regexp fname 0) && not (Str.string_match acl_file_regexp fname 0)) then  
97            begin
98              (* Filter frontend list based on acl *)
99              let acl_fqp = String.concat "." [fqp;"acl"] in
100              let acl_filter = this#make_filter acl_fqp in
101              let slice_list = 
102                match acl_filter with
103                  | None -> [] (* No ACL *) 
104                  | Some(filter) -> List.filter 
105                                      (fun fe->Hashtbl.mem filter (fe#get_slice_name ())) 
106                                      frontend_lst 
107              in 
108              let is_event = list_check evlist in
109                if (is_event Create) then
110                  begin
111                    if (is_event Isdir) then
112                      begin
113                        this#new_dir slice_list fqp this#handle_dir_event
114                      end 
115                    else
116                      (* It's a new script *)
117                      begin
118                        this#new_script slice_list fqp
119                      end
120                  end
121                else if (is_event Delete) then
122                  begin
123                    if (is_event Isdir) then
124                      begin
125                        (*this#rm_watch fqp;*)
126                        List.iter (fun frontend->
127                                     frontend#rmdir (mk_rel_path fqp)) slice_list
128                      end
129                    else List.iter (fun frontend ->
130                                      frontend#unlink (mk_rel_path fqp)) slice_list
131                  end
132            end
133          else (* regex not matched *)
134            fprintf logfd "Rejected weird entry %s\n" fname
135
136      (** Initializer - build the initial tree based on the contents of /vsys *)
137      initializer 
138      let rec build_initial_tree dir =
139        let dir_handle = opendir dir in
140        let cont = ref true in
141          while (!cont) do
142            try 
143              let curfile = readdir dir_handle in
144                if (not (this#is_acl curfile)) then
145                  begin
146                    let fqp = String.concat "/" [dir;curfile] in
147                    let acl_fqp = String.concat "." [fqp;"acl"] in
148                    let acl_filter = this#make_filter acl_fqp in
149                    let slice_list = 
150                      match acl_filter with
151                        | None -> [] (*frontend_lst -> No ACL => No Show *)
152                        | Some(filter) -> List.filter 
153                                            (fun fe->Hashtbl.mem filter (fe#get_slice_name ())) 
154                                            frontend_lst 
155                    in
156                      if (Str.string_match file_regexp curfile 0) then
157                        let s = Unix.stat fqp in
158                          begin
159                            match s.st_kind with
160                              | S_DIR ->
161                                  this#new_dir slice_list fqp this#handle_dir_event;
162                                  build_initial_tree fqp;
163                              | S_REG ->
164                                  this#new_script slice_list fqp
165                              | _ ->
166                                  logprint "Don't know what to do with %s\n" curfile
167                          end
168                  end
169            with _
170            ->cont:=false;()
171          done 
172      in
173        begin
174          build_initial_tree dir_root;
175          Dirwatcher.add_watch dir_root [S_Create;S_Delete] (this#handle_dir_event);
176        end
177    end