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