1 (** fifowatcher.ml: Routines for creating and managing fifos *)
10 (** A connected process, FIFO *)
11 type channel_pipe = Process of Unix.file_descr | Fifo of Unix.file_descr | BrokenPipe
13 (** Signed file descriptors. *)
14 type signed_fd = Infd of Unix.file_descr | Outfd of Unix.file_descr | Eventfd of Unix.file_descr
16 (** XXX This will get deprecated when we switch to inotify *)
17 let fdmap: (Unix.file_descr,string*string) Hashtbl.t = Hashtbl.create 1024
19 (** Maps pids to slice connections. Needed to clean up fds when a script dies
21 let pidmap: (int,signed_fd list) Hashtbl.t = Hashtbl.create 1024
22 let backend_prefix = ref ""
23 let open_fds: (Unix.file_descr,channel_pipe) Hashtbl.t = Hashtbl.create 1024
26 (** Receive an event from a running script. This event must be relayed to the
27 slice that invoked it.
29 @param idesc fd/fname identifier for process
31 let receive_process_event (idesc: fname_and_fd) (_: fname_and_fd) =
32 let (_,ifd) = idesc in
33 let cp = try Hashtbl.find open_fds ifd with
35 fprintf logfd "Fifo fd disappeared\n";flush logfd;raise Bug
41 printf "Received process event\n";flush Pervasives.stdout;
42 let tr0,tr1 = Unix.pipe () in
43 ignore(splice ifd tr1 4096);
44 ignore(splice tr0 fifo_outfd 4096)
46 Failure(s)->fprintf logfd "Transfer failure: %s\n" s;flush logfd
48 | _ -> fprintf logfd "Bug! Process fd received in the channel handler\n";flush logfd;raise Bug
50 let rec openentry_int fifoin fifoout (abspath:string*string) =
52 try openfile fifoin [O_RDONLY;O_NONBLOCK] 0o777 with
53 e->fprintf logfd "Error opening and connecting FIFO: %s,%o\n" fifoin 0o777;flush logfd;raise e
55 Hashtbl.replace fdmap fdin abspath;
56 Fdwatcher.add_fd (Some(fifoin),fdin) (Some(fifoout),stdout) receive_fifo_event
57 and reopenentry_int fdin fifoin fifoout =
59 Fdwatcher.del_fd fdin;
61 Hashtbl.find fdmap fdin with _ -> fprintf logfd "Bug: Phantom pipe\n";flush logfd;raise Bug
63 openentry_int fifoin fifoout abspath
65 (** receive an event from a fifo and connect to the corresponding service, or to
66 create it if it doesn't exit
67 @param eventdescriptor Name of input pipe,in descriptor
68 @param outdescriptor Name of output pipe, out descriptor
70 and receive_fifo_event eventdescriptor outdescriptor =
71 let (evfname,evfd) = eventdescriptor in
72 let (fname_other,fd_other) = outdescriptor in
73 (* Open the output pipe, or use stdout instead *)
75 match (fname_other) with
78 try openfile str [O_WRONLY;O_NONBLOCK] 0o777 with
79 _->fprintf logfd "Output pipe not open, using stdout in place of %s\n" str;flush logfd;stdout
81 | None-> fprintf logfd "Bug, nameless pipe\n";flush logfd;raise Bug
83 (* Check if the input descriptor is already registered (=> a session is open).
84 If not, register it and start a new session.*)
85 let pipe = try Hashtbl.find open_fds evfd with
87 (* Ok, need to launch script *)
88 let execpath,slice_name = Hashtbl.find fdmap evfd in
89 let (script_infd,pout) = Unix.pipe () in
90 let (pin,script_outfd) = Unix.pipe () in
91 set_nonblock script_infd;
92 ignore(sigprocmask SIG_BLOCK [Sys.sigchld]);
93 let rpid = try Some(create_process execpath [|execpath;slice_name|] pin pout pout) with e -> fprintf logfd "Error executing service: %s\n" execpath;flush logfd;None
98 (* Register fds associated with pid so that they can be cleaned up
100 Hashtbl.add pidmap pid [Infd(script_infd);Outfd(script_outfd);Eventfd(evfd)];
102 (* Connect pipe to running script *)
103 Hashtbl.add open_fds evfd (Process(script_outfd));
105 (* Connect the running script to the pipe *)
106 Hashtbl.add open_fds script_infd (Fifo(outfd));
108 (* Activate running script *)
109 Fdwatcher.add_fd (None,script_infd) (None,script_infd) receive_process_event;
111 (Process(script_outfd))
113 (* We have the connection to the process - because it was open, or because it
114 just got established *)
116 | Process(fifo_outfd) ->
119 printf "Received fifo event\n";flush Pervasives.stdout;
120 let tr0,tr1 = Unix.pipe() in
121 ignore(splice evfd tr1 4096);
122 ignore(splice tr0 fifo_outfd 4096)
125 fprintf logfd "Error connecting user to service: %s\n" str;
128 ignore(sigprocmask SIG_UNBLOCK [Sys.sigchld])
131 | Fifo(_) -> fprintf logfd "BUG! received process event from fifo\n";raise Bug
134 (** Make a pair of fifo entries *)
135 let mkentry fqp abspath perm uname =
136 fprintf logfd "Making entry %s->%s\n" fqp abspath;flush logfd;
137 let fifoin=sprintf "%s.in" fqp in
138 let fifoout=sprintf "%s.out" fqp in
139 (try Unix.unlink fifoin with _ -> ());
140 (try Unix.unlink fifoout with _ -> ());
142 let infname =(sprintf "%s.in" fqp) in
143 let outfname =(sprintf "%s.out" fqp) in
144 Unix.mkfifo infname 0o666;
145 Unix.mkfifo outfname 0o666;
146 ( (* Make the user the owner of the pipes in a non-chroot environment *)
147 if (!Globals.nochroot) then
148 let pwentry = Unix.getpwnam uname in
149 Unix.chown infname pwentry.pw_uid pwentry.pw_gid;
150 Unix.chown outfname pwentry.pw_uid pwentry.pw_gid
154 e->fprintf logfd "Error creating FIFO: %s->%s. May be something wrong at the frontend.\n" fqp fifoout;flush logfd;Failed)
156 (** Open fifos for a session. Will shutdown vsys if the fifos don't exist *)
157 let openentry fqp abspath perm =
158 let fifoin = String.concat "." [fqp;"in"] in
159 let fifoout = String.concat "." [fqp;"out"] in
160 openentry_int fifoin fifoout abspath
162 let sigchld_handle s =
163 let pid,_=Unix.waitpid [Unix.WNOHANG] 0 in
165 let sfd_list = Hashtbl.find pidmap pid in
174 Hashtbl.remove open_fds fd (* Disconnect pipe *)
176 List.iter handle_sfd sfd_list;
177 Hashtbl.remove pidmap pid
179 Not_found-> (* Do nothing, probably a grandchild *)
183 Sys.set_signal Sys.sigchld (Sys.Signal_handle sigchld_handle)