From: Sapan Bhatia Date: Thu, 7 Aug 2008 17:18:51 +0000 (+0000) Subject: Copying latest tagged version into 0.7. This should add sanity to my deployments... X-Git-Tag: vsys-0.7-19~1 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=3d99e7627607605e6a43a1545a3b4fab29b7c10b;hp=c93602207c6e6bd74671d7b88996826094b5fd56;p=vsys.git Copying latest tagged version into 0.7. This should add sanity to my deployments. Also, now trunk has leapt ahead, so this will be the stable branch. --- diff --git a/.dep b/.dep index 3ec5199..60ed53b 100644 --- a/.dep +++ b/.dep @@ -1,12 +1,22 @@ +backend.cmo: inotify.cmi globals.cmo frontend.cmo fifowatcher.cmo \ + dirwatcher.cmo +backend.cmx: inotify.cmi globals.cmx frontend.cmx fifowatcher.cmx \ + dirwatcher.cmx +conffile.cmo: globals.cmo +conffile.cmx: globals.cmx +directfifowatcher.cmo: splice.cmo inotify.cmi globals.cmo dirwatcher.cmo +directfifowatcher.cmx: splice.cmx inotify.cmi globals.cmx dirwatcher.cmx dirwatcher.cmo: inotify.cmi globals.cmo fdwatcher.cmo dirwatcher.cmx: inotify.cmi globals.cmx fdwatcher.cmx fdwatcher.cmo: globals.cmo fdwatcher.cmx: globals.cmx -fifowatcher.cmo: inotify.cmi globals.cmo fdwatcher.cmo dirwatcher.cmo -fifowatcher.cmx: inotify.cmi globals.cmx fdwatcher.cmx dirwatcher.cmx -frontend.cmo: globals.cmo fifowatcher.cmo -frontend.cmx: globals.cmx fifowatcher.cmx +fifowatcher.cmo: splice.cmo inotify.cmi globals.cmo fdwatcher.cmo \ + dirwatcher.cmo +fifowatcher.cmx: splice.cmx inotify.cmi globals.cmx fdwatcher.cmx \ + dirwatcher.cmx +frontend.cmo: globals.cmo directfifowatcher.cmo +frontend.cmx: globals.cmx directfifowatcher.cmx main.cmo: inotify.cmi globals.cmo frontend.cmo fifowatcher.cmo fdwatcher.cmo \ - dirwatcher.cmo backend.cmo + dirwatcher.cmo conffile.cmo backend.cmo main.cmx: inotify.cmi globals.cmx frontend.cmx fifowatcher.cmx fdwatcher.cmx \ - dirwatcher.cmx backend.cmx + dirwatcher.cmx conffile.cmx backend.cmx diff --git a/Makefile b/Makefile index 65e5736..b5272ca 100644 --- a/Makefile +++ b/Makefile @@ -30,11 +30,14 @@ docs: *.ml ocaml_inotify-0.4/inotify.cmxa: $(MAKE) -C ocaml_inotify-0.4 && cp -f ocaml_inotify-0.4/inotify_stubs.o ./ -vsys: ocaml_inotify-0.4/inotify.cmxa globals.cmx fdwatcher.cmx conffile.cmx dirwatcher.cmx fifowatcher.cmx frontend.cmx backend.cmx main.cmx docs - ocamlopt -I ocaml_inotify-0.4 str.cmxa unix.cmxa inotify.cmxa globals.cmx fdwatcher.cmx dirwatcher.cmx fifowatcher.cmx frontend.cmx backend.cmx str.cmxa conffile.cmx main.cmx -o vsys +splice_stub.o: splice_stub.c + gcc -c -I /usr/lib/ocaml -I /usr/lib64/ocaml splice_stub.c -o splice_stub.o -vsys.b: inotify.cma inotify.cmi globals.ml fdwatcher.ml dirwatcher.ml fifowatcher.ml frontend.ml backend.ml main.ml - ocamlc -g str.cma unix.cma inotify.cma globals.cmo fdwatcher.cmo dirwatcher.cmo fifowatcher.cmo frontend.cmo backend.cmo str.cma main.cmo -o vsys.b +vsys: ocaml_inotify-0.4/inotify.cmxa globals.cmx fdwatcher.cmx conffile.cmx splice_stub.o splice.cmx dirwatcher.cmx fifowatcher.cmx frontend.cmx backend.cmx main.cmx docs + ocamlopt -I ocaml_inotify-0.4 str.cmxa unix.cmxa inotify.cmxa globals.cmx fdwatcher.cmx dirwatcher.cmx splice.cmx splice_stub.o directfifowatcher.cmx frontend.cmx backend.cmx str.cmxa conffile.cmx main.cmx -o vsys + +vsys.b: ocaml_inotify-0.4/inotify.cma inotify.cmi globals.ml fdwatcher.ml dirwatcher.ml directfifowatcher.ml frontend.ml backend.ml main.ml + ocamlc -g str.cma unix.cma ocaml_inotify-0.4/inotify.cma globals.cmo fdwatcher.cmo dirwatcher.cmo directfifowatcher.cmo frontend.cmo backend.cmo str.cma conffile.cmo main.cmo -o vsys.b install: vsys cp vsys $(INSTALL_DIR)/usr/bin diff --git a/backend.ml b/backend.ml index 2f373c0..535c549 100644 --- a/backend.ml +++ b/backend.ml @@ -15,10 +15,6 @@ open Fifowatcher open Frontend open Printf -(** Helper functions: - -*) - (** Turn an absolute path into a relative path. *) let delete_prefix prefix str = let len = String.length str in @@ -35,7 +31,6 @@ let rec list_check lst elt = | [] -> false | car::cdr -> if (car==elt) then true else list_check cdr elt - (** The backendhandler class: defines event handlers for events in the backend backend directory. @param dir_root The location of the backend in the server context (eg. root context for vservers) @@ -45,34 +40,30 @@ class backendHandler dir_root (frontend_lst: frontendHandler list) = let mk_rel_path = delete_prefix dir_root in object(this) (** Regular expression that defines a legal script name. Filter out - * temporary files using this *) - val file_regexp = Str.regexp "[a-zA-Z][a-zA-Z0-9_\.]*" + * temporary files using it *) + val file_regexp = Str.regexp "^[a-zA-Z][a-zA-Z0-9_\.\-]*$" val acl_file_regexp = Str.regexp ".*acl$" - val dir_regexp = Str.regexp "^dir_"; - val acl_regexp = Str.regexp ".*_.*"; - (** Somebody created a new directory *) - (* XXX Race condition here *) method private new_dir slice_list fqp func = let s = Unix.stat fqp in List.iter (fun frontend-> try begin frontend#mkdir (mk_rel_path fqp) (s.st_perm); - Dirwatcher.add_watch fqp [S_Create;S_Delete] (Some(func)) + Dirwatcher.add_watch fqp [S_Create;S_Delete] func end with _ -> - printf "Could not create %s. Looks like a slice shot itself in the foot\n" fqp;flush Pervasives.stdout; + logprint "Could not create %s. Looks like a slice shot itself in the foot\n" fqp; ) slice_list (** Somebody copied in a new script *) - (* XXX Race condition here *) method private new_script slice_list fqp = let s = Unix.stat fqp in List.iter (fun frontend-> - frontend#mkentry (mk_rel_path fqp) fqp (s.st_perm)) slice_list + frontend#mkentry (mk_rel_path fqp) fqp (s.st_perm)) + slice_list method private make_filter acl_fqp = let filter = Hashtbl.create 16 in @@ -84,7 +75,7 @@ class backendHandler dir_root (frontend_lst: frontendHandler list) = with _ -> None in match next_item with - | None -> cur_filter + | None -> close_in acl_file;cur_filter | Some(item) -> Hashtbl.add cur_filter item true; read_acl cur_filter @@ -93,12 +84,14 @@ class backendHandler dir_root (frontend_lst: frontendHandler list) = with _ -> None + method is_acl fname = Str.string_match acl_file_regexp fname 0 + (** Gets called every time there's an inotify event at the backend @param dirname Name of the backend directory @param evlist Description of what happened @param fname Name of the file that the event applies to *) - method handle_dir_event dirname evlist fname = + method handle_dir_event _ dirname evlist fname = let fqp = String.concat "/" [dirname;fname] in if ((Str.string_match file_regexp fname 0) && not (Str.string_match acl_file_regexp fname 0)) then begin @@ -107,8 +100,10 @@ class backendHandler dir_root (frontend_lst: frontendHandler list) = let acl_filter = this#make_filter acl_fqp in let slice_list = match acl_filter with - | None -> frontend_lst - | Some(filter) -> List.filter (fun fe->Hashtbl.mem filter (fe#get_slice_name ())) frontend_lst + | None -> [] (* No ACL *) + | Some(filter) -> List.filter + (fun fe->Hashtbl.mem filter (fe#get_slice_name ())) + frontend_lst in let is_event = list_check evlist in if (is_event Create) then @@ -120,13 +115,6 @@ class backendHandler dir_root (frontend_lst: frontendHandler list) = else (* It's a new script *) begin - (* - if (Str.string_match dir_regexp fname 0) then - let fqp = String.concat "/" [dirname;String.sub fname 4 ((String.length fname)-4+1)] in - let real_fqp = String.concat "/" [dirname;fname] in - this#new_dir fqp this#handle_spool_event; - Hashtbl.add spools fqp real_fqp - else*) this#new_script slice_list fqp end end @@ -143,7 +131,7 @@ class backendHandler dir_root (frontend_lst: frontendHandler list) = end end else (* regex not matched *) - () + fprintf logfd "Rejected weird entry %s\n" fname (** Initializer - build the initial tree based on the contents of /vsys *) initializer @@ -152,33 +140,38 @@ class backendHandler dir_root (frontend_lst: frontendHandler list) = let cont = ref true in while (!cont) do try - let curfile = readdir dir_handle in - let fqp = String.concat "/" [dir;curfile] in - let acl_fqp = String.concat "." [fqp;"acl"] in - let acl_filter = this#make_filter acl_fqp in - let slice_list = - match acl_filter with - | None -> frontend_lst - | Some(filter) -> List.filter (fun fe->Hashtbl.mem filter (fe#get_slice_name ())) frontend_lst - in - if (Str.string_match file_regexp curfile 0 && not (Str.string_match acl_file_regexp curfile 0)) then - let s = Unix.stat fqp in - begin - match s.st_kind with - | S_DIR -> - this#new_dir slice_list fqp this#handle_dir_event; - build_initial_tree fqp; - | S_REG -> - this#new_script slice_list fqp - | _ -> - printf "Don't know what to do with %s\n" curfile;flush Pervasives.stdout - end - with - _->cont:=false;() + let curfile = readdir dir_handle in + if (not (this#is_acl curfile)) then + begin + let fqp = String.concat "/" [dir;curfile] in + let acl_fqp = String.concat "." [fqp;"acl"] in + let acl_filter = this#make_filter acl_fqp in + let slice_list = + match acl_filter with + | None -> [] (*frontend_lst -> No ACL => No Show *) + | Some(filter) -> List.filter + (fun fe->Hashtbl.mem filter (fe#get_slice_name ())) + frontend_lst + in + if (Str.string_match file_regexp curfile 0) then + let s = Unix.stat fqp in + begin + match s.st_kind with + | S_DIR -> + this#new_dir slice_list fqp this#handle_dir_event; + build_initial_tree fqp; + | S_REG -> + this#new_script slice_list fqp + | _ -> + logprint "Don't know what to do with %s\n" curfile + end + end + with _ + ->cont:=false;() done in begin build_initial_tree dir_root; - Dirwatcher.add_watch dir_root [S_Create;S_Delete] (Some(this#handle_dir_event)); + Dirwatcher.add_watch dir_root [S_Create;S_Delete] (this#handle_dir_event); end end diff --git a/conffile.ml b/conffile.ml index d7b8715..0d0f0f1 100644 --- a/conffile.ml +++ b/conffile.ml @@ -1,17 +1,37 @@ open Printf +open Globals open Scanf let split_conf_line s = sscanf s "%s %s" (fun s1 s2->(s1,s2)) +let check_dir fe = + let (vsysdir,slice) = fe in + let verdict = try Some(Unix.stat vsysdir) with + _ -> logprint "vsys directory not setup for slice %s\n" slice;None + in + match verdict with + | None->false + | Some(_) -> true + +let rec in_list elt lst = + match lst with + | car::cdr -> + if (elt = car) then true else in_list elt cdr + | [] -> false + let read_frontends f = - let f_file = try open_in f with e -> printf "Could not open config - file\n";flush Pervasives.stdout;raise e + let setup_ok = if (!Globals.failsafe) then check_dir else fun _ -> true in + let f_file = try open_in f with e -> logprint "Could not open config file\n";raise e in let rec read_conf_file cur_list = let next_line = try Some(input_line f_file) with _ -> None in match next_line with - | Some(inp_line) -> read_conf_file (split_conf_line(inp_line)::cur_list) + | Some(inp_line) -> + let fe = split_conf_line inp_line in + let new_list = if (not (in_list fe cur_list) && (setup_ok(fe))) then (fe::cur_list) else cur_list + in + read_conf_file new_list | None -> cur_list in read_conf_file [] diff --git a/directfifowatcher.ml b/directfifowatcher.ml new file mode 100644 index 0000000..e97d7c5 --- /dev/null +++ b/directfifowatcher.ml @@ -0,0 +1,167 @@ +(** directfifowatcher.ml: Routines to handle non-persistent scripts *) +(* Semantics: + * - The 'out' descriptor must be opened first + * - As soon as the backend script dies, the connection to the entry is + * closed. + * - To avoid user-inflicted pain, all entries are opened at the time + * that they are created. Reopening these entries is a little complicated + * but nevertheless sound: + * * When a script dies, its fd is reopened + * * If a script fails to execute, its fd is closed and reopened to + * beat a race that can happen when the user closes the connection + * before the script can be launched. + *) + +open Inotify +open Unix +open Globals +open Dirwatcher +open Printf +open Splice + +let close_if_open fd = (try (ignore(close fd);) with _ -> ()) + +type in_pathname = string +type directory = string +type base_pathname = string +type slice_name = string + +let direct_fifo_table: (in_pathname,(directory*base_pathname*slice_name*Unix.file_descr) option) Hashtbl.t = + Hashtbl.create 1024 + +let pidmap: (int,in_pathname * Unix.file_descr) Hashtbl.t = Hashtbl.create 1024 + +let move_gate fname = + let tmpfname=String.concat "." [fname;"tmp"] in + Unix.rename fname tmpfname; + tmpfname + +let move_ungate fname restore = + Unix.rename restore fname + +let list_check lst elt _ = + let rec list_check_rec lst = + match lst with + | [] -> false + | car::cdr -> + if (car==elt) then + true + else + list_check_rec cdr + in + list_check_rec lst + +let openentry_int fifoin = + let fdin = + try openfile fifoin [O_RDONLY;O_NONBLOCK] 0o777 with + e->logprint "Error opening and connecting FIFO: %s,%o\n" fifoin 0o777;raise e + in + fdin + +(** Open entry safely, by first masking out the file to be opened *) +let openentry_safe root_dir fqp_in backend_spec = + let restore = move_gate fqp_in in + let fd_in = openentry_int restore in + move_ungate fqp_in restore; + let (fqp,slice_name) = backend_spec in + Hashtbl.replace direct_fifo_table fqp_in (Some(root_dir,fqp,slice_name,fd_in)) + +let openentry root_dir fqp backend_spec = + let fqp_in = String.concat "." [fqp;"in"] in + openentry_safe root_dir fqp_in backend_spec + +let reopenentry fifoin = + let entry = try Hashtbl.find direct_fifo_table fifoin with _ -> None in + match entry with + | Some(dir, fqp,slice_name,fd) -> close_if_open fd;openentry_safe dir fifoin (fqp,slice_name) + | None -> () + +(* vsys is activated when a client opens an in file *) +let connect_file fqp_in = + (* Do we care about this file? *) + let entry_info = try + Hashtbl.find direct_fifo_table fqp_in with _ -> None in + match entry_info with + | Some(_,execpath,slice_name,fifo_fdin) -> + begin + let len = String.length fqp_in in + let fqp = String.sub fqp_in 0 (len-3) in + let fqp_out = String.concat "." [fqp;"out"] in + let fifo_fdout = + try openfile fqp_out [O_WRONLY;O_NONBLOCK] 0o777 with + _-> (* The client is opening the descriptor too fast *) + sleep 1;try openfile fqp_out [O_WRONLY;O_NONBLOCK] 0o777 with + _-> + logprint "%s Output pipe not open, using stdout in place of %s\n" slice_name fqp_out;stdout + in + ignore(sigprocmask SIG_BLOCK [Sys.sigchld]); + ( + clear_nonblock fifo_fdin; + let pid=try Some(create_process execpath [|execpath;slice_name|] fifo_fdin fifo_fdout fifo_fdout) with e -> None in + match pid with + | Some(pid) -> + if (fifo_fdout <> stdout) then close_if_open fifo_fdout; + Hashtbl.add pidmap pid (fqp_in,fifo_fdout) + | None ->logprint "Error executing service: %s\n" execpath;reopenentry fqp_in + ); + ignore(sigprocmask SIG_UNBLOCK [Sys.sigchld]); + end + | None -> () + + +(** Make a pair of fifo entries *) +let mkentry fqp abspath perm uname = + logprint "Making entry %s->%s\n" fqp abspath; + let fifoin=sprintf "%s.in" fqp in + let fifoout=sprintf "%s.out" fqp in + (try Unix.unlink fifoin with _ -> ()); + (try Unix.unlink fifoout with _ -> ()); + (try + let infname =(sprintf "%s.in" fqp) in + let outfname =(sprintf "%s.out" fqp) in + Unix.mkfifo infname 0o666; + Unix.mkfifo outfname 0o666; + ( (* Make the user the owner of the pipes in a non-chroot environment *) + if (!Globals.nochroot) then + let pwentry = Unix.getpwnam uname in + Unix.chown infname pwentry.pw_uid pwentry.pw_gid; + Unix.chown outfname pwentry.pw_uid pwentry.pw_gid + ); + Success + with + e->logprint "Error creating FIFO: %s->%s. May be something wrong at the frontend.\n" fqp fifoout;Failed) + + +(** Close fifos that just got removed *) +let closeentry fqp = + let fqp_in = String.concat "." [fqp;"in"] in + let entry = try Hashtbl.find direct_fifo_table fqp_in with Not_found -> None in + match entry with + | None -> () + | Some(_,_,_,fd) -> + close_if_open fd; + Hashtbl.remove direct_fifo_table fqp_in + +let sigchld_handle s = + let pid,_=Unix.waitpid [Unix.WNOHANG] 0 in + try + let fqp_in,fd_out = Hashtbl.find pidmap pid in + begin + reopenentry fqp_in + end + with _ -> () + +let rec add_dir_watch fqp = + Dirwatcher.add_watch fqp [S_Open] direct_fifo_handler +and + direct_fifo_handler wd dirname evlist fname = + let is_event = list_check evlist in + if (is_event Open Attrib) then + let fqp_in = String.concat "/" [dirname;fname] in + connect_file fqp_in + +let del_dir_watch fqp = + () + +let initialize () = + Sys.set_signal Sys.sigchld (Sys.Signal_handle sigchld_handle) diff --git a/dirwatcher.ml b/dirwatcher.ml index b33b890..53c4c1c 100644 --- a/dirwatcher.ml +++ b/dirwatcher.ml @@ -1,5 +1,5 @@ (** Watches directories for events. Agnostic to vsys semantics of backends and -frontends *) + frontends *) open Inotify open Fdwatcher open Printf @@ -8,51 +8,65 @@ open Globals (* I don't know if a wd corresponding to a deleted directory is evicted or just * leaks - fix implementation of rmdir accordingly *) - let wdmap = Hashtbl.create 1024 let fd = Inotify.init () +let rec list_check lst elt = + match lst with + | [] -> false + | car::cdr -> if (car==elt) then true else list_check cdr elt + +let pevlist evlist = + List.iter + (fun e -> + logprint "Event: %s\n" (string_of_event e)) + evlist + let handle_dir_event dirname evlist str = - let fname = String.concat "/" [dirname;str] in - printf "File: %s. " fname;List.iter - (fun e -> - printf "Event: %s\n" (string_of_event e)) - evlist; - flush Pervasives.stdout + let fname = String.concat "/" [dirname;str] in + logprint "File: %s. " fname; + pevlist evlist let add_watch dir events handler = - printf "Adding watch for %s\n" dir;flush Pervasives.stdout; let wd = Inotify.add_watch fd dir events in - Hashtbl.add wdmap wd (dir,handler) + Hashtbl.add wdmap wd (dir,Some(handler)) + (* Ignore the possibility that the whole directory can disappear and come + * back while it is masked *) let asciiz s = let rec findfirstnul str idx len = if ((idx==len) || - (str.[idx]==(char_of_int 0))) then idx - else - findfirstnul str (idx+1) len + (str.[idx]==(char_of_int 0))) then idx + else + findfirstnul str (idx+1) len in let nulterm = findfirstnul s 0 (String.length s) in String.sub s 0 nulterm let receive_event (eventdescriptor:fname_and_fd) (bla:fname_and_fd) = let (_,fd) = eventdescriptor in - let evs = Inotify.read fd in - List.iter (fun x-> - match x with - | (wd,evlist,_,Some(str)) -> - let purestr = asciiz(str) in - let (dirname,handler) = - try Hashtbl.find wdmap wd with Not_found->printf "Unknown watch descriptor\n";raise Not_found - in - ( - match handler with - | None->handle_dir_event dirname evlist purestr - | Some(handler)->handler dirname evlist purestr - ) - | _ -> ()) - evs + let evs = Inotify.read fd in + List.iter (fun x-> + match x with + | (wd,evlist,_,Some(str)) -> + begin + let purestr = asciiz(str) in + let (dirname,handler) = + try Hashtbl.find wdmap wd with Not_found->("",None) + in + match handler with + | None-> + logprint "Unhandled watch descriptor\n" + | Some(handler)-> + let fqp = String.concat "/" [dirname;purestr] in + begin + handler wd dirname evlist + purestr + end + end + | _ -> ()) + evs let initialize () = Fdwatcher.add_fd (None,fd) (None,fd) receive_event diff --git a/factory/comon_exec b/factory/comon_exec new file mode 100755 index 0000000..df6e7c0 --- /dev/null +++ b/factory/comon_exec @@ -0,0 +1,40 @@ +#!/usr/bin/perl +use strict; + +####################################################### +# +# run any (allowed) progam in the root context +# +####################################################### + +my @allowed = ("/bin/df"); # allowed commands +my $cmdline; +my $path; +my $p; + +# read command line +$cmdline = ; +chomp($cmdline); + +# identify the path +if ($cmdline =~ /\s*(.+)\s+/) { + $path = $1; +} else { + $path = $cmdline; +} + +# run the program if it's executable and allowed to run +if (-x $path) { + foreach $p (@allowed) { + if ($p eq $path) { + system($cmdline); + exit(0); + } + } + print "fatal: '$cmdline' is not allowed to run\n"; +} else { + print "fatal: $path either does not exist or is not executable\n"; +} + +# some error occurred +exit(-1); diff --git a/factory/hide_netif b/factory/hide_netif new file mode 100755 index 0000000..c7ae234 --- /dev/null +++ b/factory/hide_netif @@ -0,0 +1,6 @@ +#!/bin/sh +# Remove hide_netif network attribute. Attribute is used to hide interfaces that don't have an IP attached. + +# $Id$ + +nattribute --set --nid $1 --flag ~hide_netif diff --git a/factory/pfmount b/factory/pfmount new file mode 100755 index 0000000..0282c76 --- /dev/null +++ b/factory/pfmount @@ -0,0 +1,10 @@ +#!/bin/sh +# Mount the planetflow directory in a slice + +#mount --bind /usr/local/fprobe /vservers/$1/pf +# changed from request of Faiyaz +DEST="/vservers/$1/pf" +mount | grep "on $DEST type" > /dev/null +if [ $? -eq 1 ]; then + mount --bind /var/local/fprobe -o ro $DEST +fi diff --git a/factory/pl-ps b/factory/pl-ps new file mode 100755 index 0000000..eafdf5b --- /dev/null +++ b/factory/pl-ps @@ -0,0 +1,36 @@ +#!/usr/bin/perl +use strict; + +############################################### +# pl-ps for slicestat by KyoungSoo Park +############################################### + +my %slice_id; +my %slice; + +open THIS_PIPE, "/bin/awk -F: \'{print \$1, \$3}\' /etc/passwd |"; +while() { + if (/(.+)\s+(\d+)/) { + $slice_id{$1} = $2; + $slice{$2} = $1; + } +} +close THIS_PIPE; + +open THIS_PIPE, "/usr/sbin/vps -eo pid,user | sed 1d | awk \'{print \$1, \$2}\' | sort -k 2 |"; +while() { + if (/(\d+)\s+(.+)$/) { + my $pid = $1; + my ($id, $sl); + + if (defined($slice_id{$2})) { + $id = $slice_id{$2}; + $sl = $2; + } else { + $id = $2; + $sl = $slice{$2}; + } + print sprintf("%s %s %s\n", $id, $sl, $pid); + } +} +close THIS_PIPE; diff --git a/factory/setup-link b/factory/setup-link new file mode 100755 index 0000000..e5cbd2f --- /dev/null +++ b/factory/setup-link @@ -0,0 +1,96 @@ +#!/bin/sh +x + +IP=/sbin/ip + +SLICE=$1 +SLICEID=`id -u $SLICE` +read INDEX +read REMOTE +read KEY + +LINK=${KEY}if${INDEX} + +modprobe ip_gre +modprobe etun + +### Setup EGRE tunnel +EGRE=d$LINK +$IP tunnel add $EGRE mode gre/eth remote $REMOTE key $KEY +$IP link set $EGRE up + +### Setup etun +ETUN0=a$LINK +ETUN1=b$LINK +echo $ETUN0,$ETUN1 > /sys/module/etun/parameters/newif +ifconfig $ETUN0 mtu 1458 up +ifconfig $ETUN1 up + +### Setup bridge +BRIDGE=c$LINK +brctl addbr $BRIDGE +brctl addif $BRIDGE $EGRE +brctl addif $BRIDGE $ETUN1 +ifconfig $BRIDGE up + +### Setup iptables so that packets are visible in the vserver +iptables -t mangle -A FORWARD -o $BRIDGE -j MARK --set-mark $SLICEID + +### Create "grab link" script +GRAB=/vsys/local_grab-$ETUN0 +echo $SLICE > $GRAB.acl +rm -f $GRAB +cat > $GRAB < /sys/class/net/$ETUN0/new_ns_pid +EOF +chmod +x $GRAB + +### Create script for setting link rate +BIND=/vsys/local_rate-$ETUN0 +echo $SLICE > $BIND.acl +rm -f $BIND +cat > $BIND < $DELETE.acl +rm -f $DELETE +cat > $DELETE < /sys/module/etun/parameters/delif + +# Get rid of bridge +ifconfig $BRIDGE down +brctl delbr $BRIDGE + +# Get rid of EGRE tunnel +ip tunnel del $EGRE + +# Clean up files +rm -f $GRAB $GRAB.acl +rm -f $DELETE $DELETE.acl +rm -f $BIND $BIND.acl +EOF +chmod +x $DELETE diff --git a/factory/setup-nat b/factory/setup-nat new file mode 100755 index 0000000..64be40b --- /dev/null +++ b/factory/setup-nat @@ -0,0 +1,57 @@ +#!/bin/sh +x + +IP=/sbin/ip + +SLICE=$1 +SLICEID=`id -u $SLICE` +read KEY + +modprobe etun + +### Setup etun +ETUN0=nat$KEY +ETUN1=natx$KEY +echo $ETUN0,$ETUN1 > /sys/module/etun/parameters/newif +ifconfig $ETUN1 10.0.$KEY.1 up + +/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE +/sbin/iptables -A FORWARD -i eth0 -o $ETUN1 -m state --state RELATED,ESTABLISHED -j ACCEPT +/sbin/iptables -A FORWARD -i $ETUN1 -o eth0 -j ACCEPT + +### Create "grab link" script +GRAB=/vsys/local_grab-$ETUN0 +echo $SLICE > $GRAB.acl +rm -f $GRAB +cat > $GRAB < /sys/class/net/$ETUN0/new_ns_pid +EOF +chmod +x $GRAB + +### Create "delete link" script +DELETE=/vsys/local_delete-$ETUN0 +echo $SLICE > $DELETE.acl +rm -f $DELETE +cat > $DELETE < /sys/module/etun/parameters/delif + +# Clean up files +rm -f $GRAB $GRAB.acl +rm -f $DELETE $DELETE.acl + +EOF +chmod +x $DELETE + diff --git a/factory/vtop b/factory/vtop new file mode 100755 index 0000000..00e44c0 --- /dev/null +++ b/factory/vtop @@ -0,0 +1,12 @@ +#!/usr/bin/perl +use strict; + +############################################### +# vtop for slicestat by KyoungSoo Park +############################################### + +open THIS_PIPE, "/usr/sbin/vtop bn1 |"; +while() { + print; +} +close THIS_PIPE; diff --git a/fdwatcher.ml b/fdwatcher.ml index eb9559c..a85baaa 100644 --- a/fdwatcher.ml +++ b/fdwatcher.ml @@ -1,34 +1,35 @@ (** Fdwatcher - The main event loop. Agnostic to the type of file descriptors - involved.*) + involved.*) open Printf open Globals +open Printexc let fdset = ref [] let cbtable = Hashtbl.create 1024 -(* The in descriptor is always open. Thanks to the broken semantics of - * fifo outputs, the out descriptor must be opened a nouveau whenever we - * want to send out data, and so we keep the associated filename as well. - * Same with input fifos. Yipee.*) let add_fd (evpair:fname_and_fd) (fd_other:fname_and_fd) (callback:fname_and_fd->fname_and_fd->unit) = let (fname,fd) = evpair in - fdset := (fd::!fdset); - Hashtbl.replace cbtable fd (callback,(evpair,fd_other)) + fdset := (fd::!fdset); + Hashtbl.replace cbtable fd (callback,(evpair,fd_other)) let del_fd fd = fdset:=List.filter (fun l->l<>fd) !fdset; - flush Pervasives.stdout + flush logfd let start_watch () = - while (true) - do - let (fds,_,_) = try Unix.select !fdset [] [] (-1.) - with e-> - ([],[],[]) - in - List.iter (fun elt-> - let (func,(evd,fd_other)) = Hashtbl.find cbtable elt in - func evd fd_other) fds - done - + while (true) + do + let (fds,_,_) = try Unix.select !fdset [] [] (-1.) + with e-> + ([],[],[]) + in + List.iter (fun elt-> + let (func,(evd,fd_other)) = Hashtbl.find cbtable elt in + try (* Never fail *) + func evd fd_other + with e-> + let wtf = Printexc.to_string e in + logprint "%s\n" wtf + ) fds + done diff --git a/fifowatcher.ml b/fifowatcher.ml index dc79dad..d9625b6 100644 --- a/fifowatcher.ml +++ b/fifowatcher.ml @@ -5,21 +5,23 @@ open Unix open Globals open Dirwatcher open Printf +open Splice (** A connected process, FIFO *) -type channel_pipe = Process of out_channel | Fifo of out_channel | BrokenPipe -(** Signed file descriptors. Usually, we'll make sure that they're not - mistreated *) +type channel_pipe = Process of Unix.file_descr | Fifo of Unix.file_descr | BrokenPipe + +(** Signed file descriptors. *) type signed_fd = Infd of Unix.file_descr | Outfd of Unix.file_descr | Eventfd of Unix.file_descr +(** XXX This will get deprecated when we switch to inotify *) let fdmap: (Unix.file_descr,string*string) Hashtbl.t = Hashtbl.create 1024 + (** Maps pids to slice connections. Needed to clean up fds when a script dies with EPIPE *) let pidmap: (int,signed_fd list) Hashtbl.t = Hashtbl.create 1024 let backend_prefix = ref "" let open_fds: (Unix.file_descr,channel_pipe) Hashtbl.t = Hashtbl.create 1024 - (** Receive an event from a running script. This event must be relayed to the slice that invoked it. @@ -29,32 +31,31 @@ let receive_process_event (idesc: fname_and_fd) (_: fname_and_fd) = let (_,ifd) = idesc in let cp = try Hashtbl.find open_fds ifd with Not_found-> - printf "Fifo fd disappeared\n";flush Pervasives.stdout;raise Bug + fprintf logfd "Fifo fd disappeared\n";flush logfd;raise Bug in match (cp) with - | Fifo(fifo_outchan) -> - let process_inchan = in_channel_of_descr ifd in - let cont = ref true in - while (!cont) do - try - let curline = input_line process_inchan in - fprintf fifo_outchan "%s\n" curline;flush fifo_outchan - with - | End_of_file|Sys_blocked_io|Unix_error(EPIPE,_,_)|Unix_error(EBADF,_,_) -> - begin - cont:=false - end - | Unix_error(_,s1,s2) -> printf "Unix error %s - %s\n" s1 s2;flush Pervasives.stdout;cont:=false - | Sys_error(s) -> (* We get this error if the EPIPE comes before the EOF marker*) cont:=false - | e -> printf "Error - received unexpected event from file system !!!\n";raise e - done - | _ -> printf "Bug! Process fd received in the channel handler\n";flush Pervasives.stdout;raise Bug - + | Fifo(fifo_outfd) -> + begin + try + printf "Received process event\n";flush Pervasives.stdout; + (* transferred = 4096 => there were at least 4096 bytes in the + * stream, so we should try again. + * transferred < 4096 => EAGAIN => either this is all the data we + * have (move on) + * OR XXX the receiver is blocking (supposedly this can't happen) *) + let transferred = ref 4096 in + while (!transferred == 4096) do + transferred:=tee ifd fifo_outfd 4096 + done; + with + Failure(s)->fprintf logfd "Transfer failure: %s\n" s;flush logfd + end + | _ -> fprintf logfd "Bug! Process fd received in the channel handler\n";flush logfd;raise Bug let rec openentry_int fifoin fifoout (abspath:string*string) = let fdin = try openfile fifoin [O_RDONLY;O_NONBLOCK] 0o777 with - e->printf "Error opening and connecting FIFO: %s,%o\n" fifoin 0o777;flush Pervasives.stdout;raise e + e->fprintf logfd "Error opening and connecting FIFO: %s,%o\n" fifoin 0o777;flush logfd;raise e in Hashtbl.replace fdmap fdin abspath; Fdwatcher.add_fd (Some(fifoin),fdin) (Some(fifoout),stdout) receive_fifo_event @@ -62,9 +63,10 @@ and reopenentry_int fdin fifoin fifoout = close fdin; Fdwatcher.del_fd fdin; let abspath = try - Hashtbl.find fdmap fdin with _ -> printf "Bug: Phantom pipe\n";flush Pervasives.stdout;raise Bug + Hashtbl.find fdmap fdin with _ -> fprintf logfd "Bug: Phantom pipe\n";flush logfd;raise Bug in openentry_int fifoin fifoout abspath + (** receive an event from a fifo and connect to the corresponding service, or to create it if it doesn't exit @param eventdescriptor Name of input pipe,in descriptor @@ -79,9 +81,9 @@ and receive_fifo_event eventdescriptor outdescriptor = | Some(str)-> ( try openfile str [O_WRONLY;O_NONBLOCK] 0o777 with - _->printf "Output pipe not open, using stdout in place of %s\n" str;flush Pervasives.stdout;stdout + _->fprintf logfd "Output pipe not open, using stdout in place of %s\n" str;flush logfd;stdout ) - | None-> printf "Bug, nameless pipe\n";flush Pervasives.stdout;raise Bug + | None-> fprintf logfd "Bug, nameless pipe\n";flush logfd;raise Bug in (* Check if the input descriptor is already registered (=> a session is open). If not, register it and start a new session.*) @@ -92,7 +94,8 @@ and receive_fifo_event eventdescriptor outdescriptor = let (script_infd,pout) = Unix.pipe () in let (pin,script_outfd) = Unix.pipe () in set_nonblock script_infd; - let rpid = try Some(create_process execpath [|execpath;slice_name|] pin pout pout) with e -> printf "Error executing service: %s\n" execpath;flush Pervasives.stdout;None + ignore(sigprocmask SIG_BLOCK [Sys.sigchld]); + 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 in match rpid with | None-> BrokenPipe @@ -102,52 +105,45 @@ and receive_fifo_event eventdescriptor outdescriptor = Hashtbl.add pidmap pid [Infd(script_infd);Outfd(script_outfd);Eventfd(evfd)]; (* Connect pipe to running script *) - Hashtbl.add open_fds evfd (Process(out_channel_of_descr script_outfd)); + Hashtbl.add open_fds evfd (Process(script_outfd)); (* Connect the running script to the pipe *) - Hashtbl.add open_fds script_infd (Fifo(out_channel_of_descr outfd)); + Hashtbl.add open_fds script_infd (Fifo(outfd)); (* Activate running script *) Fdwatcher.add_fd (None,script_infd) (None,script_infd) receive_process_event; - (Process(out_channel_of_descr script_outfd)) + (Process(script_outfd)) in - (* We have the connection to the process - because it was open, or because it - just got established *) - let inchan_fd = in_channel_of_descr evfd in + (* We have the connection to the process - because it was open, or because it + just got established *) match (pipe) with - | Process(out_channel) -> - let cont = ref true in - while (!cont) do - try - printf "Reading...\n";flush Pervasives.stdout; - let curline = input_line inchan_fd in - fprintf out_channel "%s\n" curline;flush out_channel - with - |End_of_file-> - ( - match (evfname,fname_other) with - | Some(str1),Some(str2)-> - printf "Reopening entry\n";flush Pervasives.stdout; - reopenentry_int evfd str1 str2 - | Some(str1),None -> - printf "Bug, nameless pipe\n";flush Pervasives.stdout;raise Bug - | None,_ -> - printf "Race condition -> user deleted file before closing it. Clever ploy, but won't work.\n"; - flush Pervasives.stdout - ); - cont:=false - |Sys_blocked_io ->printf "Sysblockedio\n";flush Pervasives.stdout; - cont:=false - | _ ->printf "Bug: unhandled exception\n";flush Pervasives.stdout;raise Bug - done + | Process(fifo_outfd) -> + begin + try + let transferred = ref 4096 in + while (!transferred == 4096) do + begin + transferred:=tee evfd fifo_outfd 4096; + printf "Transferred: %d\n" !transferred;flush Pervasives.stdout + end + done; + with Failure(str) -> + begin + fprintf logfd "Error connecting user to service: %s\n" str; + flush logfd + end; + ignore(sigprocmask SIG_UNBLOCK [Sys.sigchld]); + printf "Out of the loop\n";flush Pervasives.stdout + + end | BrokenPipe -> () - | Fifo(_) -> printf "BUG! received process event from fifo\n";raise Bug + | Fifo(_) -> fprintf logfd "BUG! received process event from fifo\n";raise Bug (** Make a pair of fifo entries *) let mkentry fqp abspath perm uname = - printf "Making entry %s->%s\n" fqp abspath;flush Pervasives.stdout; + fprintf logfd "Making entry %s->%s\n" fqp abspath;flush logfd; let fifoin=sprintf "%s.in" fqp in let fifoout=sprintf "%s.out" fqp in (try Unix.unlink fifoin with _ -> ()); @@ -158,14 +154,14 @@ let mkentry fqp abspath perm uname = Unix.mkfifo infname 0o666; Unix.mkfifo outfname 0o666; ( (* Make the user the owner of the pipes in a non-chroot environment *) - if (!Globals.nochroot) then - let pwentry = Unix.getpwnam uname in - Unix.chown infname pwentry.pw_uid pwentry.pw_gid; - Unix.chown outfname pwentry.pw_uid pwentry.pw_gid + if (!Globals.nochroot) then + let pwentry = Unix.getpwnam uname in + Unix.chown infname pwentry.pw_uid pwentry.pw_gid; + Unix.chown outfname pwentry.pw_uid pwentry.pw_gid ); Success with - e->printf "Error creating FIFO: %s->%s. May be something wrong at the frontend.\n" fqp fifoout;flush Pervasives.stdout;Failed) + e->fprintf logfd "Error creating FIFO: %s->%s. May be something wrong at the frontend.\n" fqp fifoout;flush logfd;Failed) (** Open fifos for a session. Will shutdown vsys if the fifos don't exist *) let openentry fqp abspath perm = diff --git a/frontend.ml b/frontend.ml index 7cc0c16..ad36172 100644 --- a/frontend.ml +++ b/frontend.ml @@ -3,7 +3,7 @@ open Printf open Unix open Globals -open Fifowatcher +open Directfifowatcher (** frontendhandler class: Methods to create and unlink pipes and directories @param root_dir vsys directory inside a slice @@ -18,55 +18,79 @@ object(this) @param abspath Absolute path of the entry @param perm Permissions of the entry at the frontend *) method mkentry (rp:relpath) abspath perm = - let realperm = perm land (lnot 0o111) in - match rp with Relpath(rel) -> - let fqp = String.concat "/" [root_dir;rel] in - let res = Fifowatcher.mkentry fqp abspath realperm slice_name in - match res with - | Success -> - Fifowatcher.openentry fqp (abspath,slice_name) realperm - | _ -> () + let realperm = perm land (lnot 0o111) in + match rp with Relpath(rel) -> + let fqp = String.concat "/" [root_dir;rel] in + let res = Directfifowatcher.mkentry fqp abspath realperm slice_name in + match res with + | Success -> + Directfifowatcher.openentry root_dir fqp (abspath,slice_name) + | _ -> () (** A new directory was created at the backend, make a corresponding directory at the frontend. Refer to mkentry for parameters *) method mkdir rp perm = match rp with Relpath(rel) -> - let fqp = String.concat "/" [root_dir;rel] in - try - let s = Unix.stat fqp in - if (s.st_kind<>S_DIR) then - begin - Unix.unlink fqp; - Unix.mkdir fqp perm - end - else if (s.st_perm <> perm) then - begin - printf "Removing directory %s\n" fqp; - flush Pervasives.stdout; - Unix.rmdir fqp; - Unix.mkdir fqp perm - end - with Unix.Unix_error(_,_,_) -> - Unix.mkdir fqp perm + let fqp = String.concat "/" [root_dir;rel] in + try + let s = Unix.stat fqp in + if (s.st_kind<>S_DIR) then + begin + Unix.unlink fqp; + Unix.mkdir fqp perm + end + else if (s.st_perm <> perm) then + begin + Unix.rmdir fqp; + Unix.mkdir fqp perm + end; + with Unix.Unix_error(_,_,_) -> + Unix.mkdir fqp perm; + Directfifowatcher.add_dir_watch fqp (** Functions corresponding to file deletion/directory removal *) (** *) method unlink rp = match rp with Relpath(rel) -> - let fqp1 = String.concat "/" [root_dir;rel;".in"] in - let fqp2 = String.concat "/" [root_dir;rel;".out"] in - try - Unix.unlink fqp1; - Unix.unlink fqp2 - with _ -> - printf "Hm. %s disappeared. Looks like slice %s shot itself in the foot\n" fqp1 (this#get_slice_name ());flush Pervasives.stdout + let fqp = String.concat "/" [root_dir;rel] in + let fqp_in = String.concat "." [fqp;"in"] in + let fqp_out = String.concat "." [fqp;"out"] in + Directfifowatcher.closeentry fqp; + try + Unix.unlink fqp_in; + Unix.unlink fqp_out + with _ -> + logprint "Hm. %s disappeared. Looks like slice %s shot itself in the foot\n" fqp (this#get_slice_name ()) method rmdir rp = match rp with Relpath(rel) -> - let fqp = String.concat "/" [root_dir;rel] in - try - Unix.rmdir fqp - with _ -> - printf "Hm. %s disappeared. Looks like slice %s shot itself in the foot\n" fqp (this#get_slice_name ());flush Pervasives.stdout + let fqp = String.concat "/" [root_dir;rel] in + Directfifowatcher.del_dir_watch fqp; + try + Unix.rmdir fqp + with _ -> + logprint "Hm. %s disappeared or not empty. Looks like slice %s shot itself in the foot\n" fqp (this#get_slice_name ()) + + initializer + ( + try + let s = Unix.stat root_dir in + if (s.st_kind<>S_DIR) then + begin + Unix.unlink root_dir; + Unix.mkdir root_dir 0o700 + end + else if (s.st_perm <> 0o700) then + begin + Unix.rmdir root_dir; + Unix.mkdir root_dir 0o700 + end; + with Unix.Unix_error(_,_,_) -> + begin + try + Unix.mkdir root_dir 0o700; + with _ -> (); + end); + Directfifowatcher.add_dir_watch root_dir end diff --git a/globals.ml b/globals.ml index 4e2d332..29054a6 100644 --- a/globals.ml +++ b/globals.ml @@ -1,12 +1,19 @@ (** Some things that didn't fit in elsewhere *) let backend = ref "" let debug = ref true -let vsys_version = "0.5" +let vsys_version = "0.7" let nochroot = ref false let conffile = ref "" +let pid_filepath = ref "/var/run/vsys.pid" +let log_filepath = ref "/var/log/vsys" +let failsafe = ref false -type result = Success | Failed +let logfd = open_out_gen [Open_append;Open_creat] 0o644 !log_filepath +let logprint fmt = Printf.fprintf logfd (fmt ^^ "%!") +let debprint fmt = if (!debug) then Printf.fprintf logfd (fmt ^^ "%!") +let print fmt = Printf.printf (fmt ^^ "%!") +type result = Success | Failed type fname_and_fd = string option * Unix.file_descr (* Relative path, never precededed by a '/' *) diff --git a/inotify_stubs.o b/inotify_stubs.o deleted file mode 100644 index 82c7c60..0000000 Binary files a/inotify_stubs.o and /dev/null differ diff --git a/main.ml b/main.ml index 391074d..872dc59 100644 --- a/main.ml +++ b/main.ml @@ -4,37 +4,61 @@ open Printf open Inotify open Backend open Frontend -open Fifowatcher open Conffile let input_file_list = ref [] let cur_dir = ref "" let cur_slice = ref "" +let daemonize = ref false let cmdspeclist = [ + ("-daemon",Arg.Set(daemonize), "Daemonize"); ("-conffile",Arg.Set_string(Globals.conffile), "Config file"); ("-backend",Arg.Set_string(Globals.backend), "Backend directory"); - ("-frontend",Arg.Tuple[Arg.String(fun s->cur_dir:=s);Arg.String(fun s->cur_slice:=s;input_file_list:=(!cur_dir,!cur_slice)::!input_file_list)], "frontendN,slicenameN"); - ("-nochroot",Arg.Set(Globals.nochroot), "Run in non-chroot environment") + ("-frontend",Arg.Tuple[Arg.String(fun s->cur_dir:=s); + Arg.String(fun s->cur_slice:=s; + input_file_list:=(!cur_dir,!cur_slice)::!input_file_list)], + "frontendN,slicenameN"); + ("-nochroot",Arg.Set(Globals.nochroot), "Run in non-chroot environment"); + ("-failsafe",Arg.Set(Globals.failsafe), "Never crash. Be stupid, but never crash. Use at your own risk."); ] -let cont = ref true - let _ = - printf "Vsys v%s\n" Globals.vsys_version;flush stdout; + logprint "Starting Vsys v%s\n" Globals.vsys_version; + print "Starting Vsys v%s\n" Globals.vsys_version; Arg.parse cmdspeclist (fun x->()) "Usage: vsys "; if (!Globals.backend == "") then - printf "Try vsys --help\n" + printf "Try vsys --help\n" else begin - Dirwatcher.initialize (); - Fifowatcher.initialize (); - if (!Globals.conffile != "") then - let frontends = Conffile.read_frontends !Globals.conffile in - input_file_list:=List.concat [!input_file_list;frontends]; + if (!daemonize) then + begin + print "Daemonizing\n"; + let child = Unix.fork () in + if (child <> 0) then + begin + let pidfile = open_out !Globals.pid_filepath in + fprintf pidfile "%d" child; + close_out pidfile; + exit(0) + end + end; + + Dirwatcher.initialize (); + Directfifowatcher.initialize (); + + if (!Globals.conffile <> "") then + begin + let frontends = Conffile.read_frontends !Globals.conffile in + input_file_list:=List.concat [!input_file_list;frontends] + end; - let felst = List.map (fun lst->let (x,y)=lst in printf "Slice %s (%s)\n" x y;flush Pervasives.stdout;new frontendHandler lst) !input_file_list in - let _ = new backendHandler !Globals.backend felst in - Fdwatcher.start_watch () + let felst = List.map + (fun lst->let (x,y)=lst in + logprint "Slice %s (%s)\n" x y; + new frontendHandler lst) + !input_file_list in + let _ = new backendHandler !Globals.backend felst in + Fdwatcher.start_watch () end diff --git a/ocaml_inotify-0.4/Makefile b/ocaml_inotify-0.4/Makefile index 7dd187a..ea4dbe6 100644 --- a/ocaml_inotify-0.4/Makefile +++ b/ocaml_inotify-0.4/Makefile @@ -33,7 +33,7 @@ inotify.cmxa: inotify_stubs.o inotify.cmx $(OCAMLOPT) $(OCAMLOPTFLAGS) -c -o $@ $< %.o: %.c - $(CC) -I /usr/lib/ocaml $(CFLAGS) -c -o $@ $< + $(CC) -I /usr/lib/ocaml -I /usr/lib64/ocaml $(CFLAGS) -c -o $@ $< test.inotify: inotify.cmxa test.inotify.ml $(OCAMLOPT) -o $@ unix.cmxa $+ diff --git a/splice.ml b/splice.ml new file mode 100644 index 0000000..3e1dfad --- /dev/null +++ b/splice.ml @@ -0,0 +1,7 @@ +open Unix + +external splice : Unix.file_descr -> Unix.file_descr -> int -> int + = "stub_splice" + +external tee : Unix.file_descr -> Unix.file_descr -> int -> int + = "stub_tee" diff --git a/splice_stub.c b/splice_stub.c new file mode 100644 index 0000000..61fbd56 --- /dev/null +++ b/splice_stub.c @@ -0,0 +1,40 @@ +/* This module allows data between vsys clients and servers to be copied in kernel - + * and some of these copies to be eliminated through page rewrites */ + +#define SPLICE_SYSCALL 313 +#define TEE_SYSCALL 315 +#define SPLICE_F_NONBLOCK 0x02 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CAMLprim value stub_splice(value fd_in, value fd_out, value len) +{ + CAMLparam3(fd_in, fd_out, len); + long ret; + ret = syscall(SPLICE_SYSCALL, Int_val(fd_in), NULL, Int_val(fd_out),NULL, Int_val(len), SPLICE_F_NONBLOCK); + if (ret == -1 && errno!=EAGAIN) { + caml_failwith("Splice system call returned -1"); + } + CAMLreturn(Val_int(ret)); +} + +CAMLprim value stub_tee(value fd_in, value fd_out, value len) +{ + CAMLparam3(fd_in, fd_out, len); + long ret; + ret = syscall(TEE_SYSCALL,Int_val(fd_in), Int_val(fd_out), Int_val(len), SPLICE_F_NONBLOCK); + if (ret == -1 && errno!=EAGAIN) { + caml_failwith(strerror(errno)); + } + CAMLreturn(Val_int(ret)); +} diff --git a/tests/vsys-test.txt b/tests/vsys-test.txt new file mode 100644 index 0000000..34b1797 --- /dev/null +++ b/tests/vsys-test.txt @@ -0,0 +1,15 @@ +Description: + +Subtest #1 - + +Create a new vsys entry, expect it to show up in a slice + +Subtest #2 - + +Connect to the entry, receive output, validate it and repeat the process 100 +times. + +Subtest #3 - + +Clean up the entry and expect it to disappear from within the slice. + diff --git a/tests/vsys_conctest.c b/tests/vsys_conctest.c new file mode 100644 index 0000000..2d174a0 --- /dev/null +++ b/tests/vsys_conctest.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + FILE *fp = NULL, *fp_in = NULL; + FILE *out_fp = NULL, *diff_fp = NULL; + const char* topcmd = "fe/test.out"; + const char* top_in_file = "fe/test.in"; + char buf[4096]; + int fd_in = -1, fd_out; + int res; + int flag,flag2; + int count = 1; + struct timeval tv={.tv_sec=5,.tv_usec=0}; + + while (count < 1000000) { + fd_set readSet; + int res; + int nlines=0; + + //usleep(200); + printf("(%d)", count);fflush(stdout); + if ((fd_out = open(topcmd, O_RDONLY | O_NONBLOCK)) < 0) { + fprintf(stderr, "error executing top\n"); + exit(-1); + } + + //printf("((0))");fflush(stdout); + while ((fd_in = open(top_in_file, O_WRONLY| O_NONBLOCK)) < 0) { + fprintf(stderr, "Waiting for %s (%s)\n", top_in_file,strerror(errno)); + usleep (50); + } + + //printf("(1)"); + if ((flag = fcntl(fd_out, F_GETFL)) == -1) { + printf("fcntl get failed\n"); + exit(-1); + } + //printf("(2)"); + + //printf("(3)"); + if ((flag2 = fcntl(fd_in, F_GETFL)) == -1) { + printf("fcntl get failed\n"); + exit(-1); + } + //printf("(4)"); + + + while (1) { + FD_ZERO(&readSet); + FD_SET(fd_out, &readSet); + + //printf("(5)"); + res = select(fd_out + 1, &readSet, NULL, NULL, NULL); + //printf("(6)"); + if (res < 0) { + if (errno == EINTR || errno == EAGAIN) { + printf("."); + continue; + } + fprintf(stderr,"select failed errno=%d errstr=%s\n", errno, strerror(errno)); + exit(-1); + } + break; /* we're done */ + } + + //printf("(7)"); + if (fcntl(fd_out, F_SETFL, flag & ~O_NONBLOCK) == -1) { + printf("fcntl set failed\n"); + exit(-1); + } + //printf("(8)"); + + //printf("(9)"); + if ((flag = fcntl(fd_out, F_GETFL)) == -1) { + printf("fcntl get failed\n"); + exit(-1); + } + //printf("(10)"); + + + //printf("(11)"); + if (fcntl(fd_in, F_SETFL, flag2 & ~O_NONBLOCK) == -1) { + printf("fcntl set failed\n"); + exit(-1); + } + + //printf("(11)"); + if ((flag2 = fcntl(fd_in, F_GETFL)) == -1) { + printf("fcntl get failed\n"); + exit(-1); + } + //printf("(12)"); + + if (flag & O_NONBLOCK == 0) { + printf("fd_out still nonblocking\n"); + exit(-1); + } + + if (flag & O_NONBLOCK == 0) { + printf("fd_in still nonblocking\n"); + exit(-1); + } + if ((fp = fdopen(fd_out, "r")) == NULL) { + printf("fdopen failed\n"); + exit(-1); + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + nlines++; + } + + if (nlines<5) { + printf("Test returned different results - run again to verify\n"); + exit(-1); + } + + fclose(fp); + close(fd_in); + close(fd_out); + count++; + } + printf("test successful.\n"); + exit(0); + +} diff --git a/tests/vsys_launch.pl b/tests/vsys_launch.pl new file mode 100644 index 0000000..204b8dc --- /dev/null +++ b/tests/vsys_launch.pl @@ -0,0 +1,43 @@ +#!/usr/bin/perl + +# Subtest #1 Create new vsys entry + +print "Creating entries...\t"; + +$vsys_entry="#!/bin/bash\n\ncat /etc/passwd"; +$vsys_entry_acl = "/vservers/pl_netflow pl_netflow"; + +open ACL,">/vsys/test.acl" || die ("Could not create acl for test entry."); +print ACL $vsys_entry_acl; +close ACL; + +open FIL,">/vsys/test" || die ("Could not create test entry."); +print FIL $vsys_entry; +close $vsys_entry; + +chmod 0755,"/vsys/test"; + +# Check if it has shown up + +(-f "/vservers/pl_netflow/test.in") || die ("in file didn't show up in the slice"); +(-f "/vservers/pl_netflow/test.out") || die ("out file didn't show up in the slice"); + +# OK, SUBTEST #1 SUCCEEDED +print "(success)\n"; + +# Subtest #2 + +print "Multiple-connection test...\t"; +system("su -c ./vsys_conctest pl_netflow -"); +($? && die ("Multiple-connection test failed\n")); + + +# OK, SUBTEST #2 SUCCEEDED +print "(success)\n"; + +# Subtest #3 +unlink "/vsys/test.acl"; +unlink "/vsys/test"; + +(-f "/vservers/pl_netflow/test.in" || -f "/vservers/pl_netflow/test.out") && die ("cleanup failed"); + diff --git a/vsys-initscript b/vsys-initscript index c448b52..cfeba9a 100755 --- a/vsys-initscript +++ b/vsys-initscript @@ -1,29 +1,56 @@ #!/bin/bash -# vsys initscript -# Author: sapanb +# +# chkconfig: 345 84 02 +# description: Vsys file descriptor abritrator startup. +# +# Sapan Bhatia +# +# $Id$ +# $HeadURL$ +# +name="vsys" -backend="/vsys" +# Source function library. +. /etc/rc.d/init.d/functions + +if [ -f /etc/sysconfig/$name ]; then + . /etc/sysconfig/$name +fi + +backend=${BACKEND-/vsys} + +conf=${CONF-/etc/$name.conf} +pidfile=${PIDFILE-/var/run/$name.pid} +lockfile=${LOCKFILE-/var/lock/subsys/$name} +RETVAL=0 + +vsys=${VSYS- "/usr/bin/vsys -failsafe -backend $backend -conffile $conf -daemon"} case "$1" in start) - echo -n "Starting vsys:" + echo -n "Starting $name:" if [ ! -d $backend ]; then mkdir $backend; fi - find /vservers -maxdepth 1 -type d | perl -e 'while (<>) {if (/(\/vservers\/(.*_.*))/) {$dir=$1;if (not (-d "$dir/vsys")) {mkdir "$dir/vsys";}}}' - frontends=`find /vservers -maxdepth 1 -type d | perl -e 'while (<>) {if (/(\/vservers\/(.*_.*))/) {$dir=$1;$slice=$2;printf "-frontend $dir/vsys $slice "}}'` - vsys_command="/usr/bin/vsys -backend $backend $frontends" - echo $vsys_command - $vsys_command + daemon --check=vsys $vsys + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch ${lockfile} ;; stop) - echo -n "Stopping $prog:" - killall vsys - exit 0 + echo -n "Stopping $name:" + killproc $name + #killproc $name - Why were there 2 instances of this? :-| + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f ${lockfile} ${pidfile} + ;; + restart) + $0 stop + $0 start + RETVAL=$? ;; *) - echo $"Usage: $0 {start|stop}" + echo $"Usage: $0 {start|stop|restart}" exit 1 ;; esac -# Source function library. -. /etc/rc.d/init.d/functions diff --git a/vsys.conf b/vsys.conf new file mode 100644 index 0000000..3312eef --- /dev/null +++ b/vsys.conf @@ -0,0 +1 @@ +/vservers/pl_netflow/vsys pl_netflow diff --git a/vsys.logrotate b/vsys.logrotate new file mode 100644 index 0000000..1eb2c45 --- /dev/null +++ b/vsys.logrotate @@ -0,0 +1,8 @@ +/var/log/netflow.log { + copytruncate + compress + daily + notifempty + rotate 5 + missingok +} diff --git a/vsys.spec b/vsys.spec index 549ea90..0842d54 100644 --- a/vsys.spec +++ b/vsys.spec @@ -3,17 +3,21 @@ # # RPM spec file # -# $Id: vsys.spec,v 1.40 2007/04/03 02:08:55 mef Exp $ +# $Id$ # +%define url $URL$ + %define name vsys -%define version 0.6 -%define release 2%{?pldistro:.%{pldistro}}%{?date:.%{date}} +%define version 0.7 +%define taglevel 18 + +%define release %{taglevel}%{?pldistro:.%{pldistro}}%{?date:.%{date}} Vendor: PlanetLab Packager: PlanetLab Central -Distribution: PlanetLab 4.0 -URL: http://cvs.planet-lab.org/cvs/vsys +Distribution: PlanetLab %{plrelease} +URL: %(echo %{url} | cut -d ' ' -f 2) Summary: Vsys filesystem Name: %{name} @@ -25,7 +29,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot #Requires: BuildRequires: inotify-tools-devel BuildRequires: ocaml -BuildRequires: ocaml-ocamldocs +BuildRequires: ocaml-docs Source0: vsys-%{version}.tar.gz @@ -45,8 +49,13 @@ make %install mkdir -p $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/etc/init.d +mkdir -p $RPM_BUILD_ROOT/vsys +cp factory/* $RPM_BUILD_ROOT/vsys cp vsys $RPM_BUILD_ROOT/usr/bin cp vsys-initscript $RPM_BUILD_ROOT/etc/init.d/vsys +cp vsys.conf $RPM_BUILD_ROOT/etc + +install -D -m 644 vsys.logrotate $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/vsys %clean rm -rf $RPM_BUILD_ROOT @@ -54,11 +63,84 @@ rm -rf $RPM_BUILD_ROOT %files /usr/bin/vsys /etc/init.d/vsys +/vsys/* +%config(noreplace) /etc/vsys.conf +%{_sysconfdir}/logrotate.d/vsys %post chkconfig --add vsys chkconfig vsys on +if [ "$PL_BOOTCD" != "1" ] ; then + service vsys restart +fi %postun %changelog +* Thu Jul 17 2008 Sapan Bhatia - vsys-0.7-18 +- Change for someone at Imperial.ac.uk, who wants access to Netflow data. + +* Tue Jul 15 2008 Sapan Bhatia - vsys-0.7-17 +- * Don't kill vsys twice on restarts, do it only once +- * Restart vsys following a reinstall + +* Wed Jul 02 2008 Thierry Parmentelat - vsys-0.7-16 +- Usability changes that are necessary for the stability of CoMon + +* Wed Jun 25 2008 Stephen Soltesz - vsys-0.7-15 +- added patch to pl-ps needed by slicestat +- +- + +* Mon Jun 23 2008 Sapan Bhatia - vsys-0.7-14 +- This change is an attempt to fix unexpected blocking after many days of uptime, reported by KyoungSoo. + +* Thu Jun 19 2008 Stephen Soltesz - vsys-0.7-13 +- accept '-' in filenames also +- + +* Wed Jun 18 2008 Stephen Soltesz - vsys-0.7-12 +- don't overwrite the config file that already exists. +- + +* Wed Jun 18 2008 Sapan Bhatia - vsys-0.7-11 +- Suppress some temp file that RPM creates frmo showing up as a vsys script. +- +- + +* Wed Jun 18 2008 Sapan Bhatia - vsys-0.7-10 +- Changed a policy in vsys. When an acl is empty, the script doesn't show up in ANY slice. The previous behavior was for +- it to show up in all slices. +- +- + +* Wed Jun 18 2008 Sapan Bhatia - vsys-0.7-9 +- Added a vsys script for CoMon. +- + +* Mon Jun 16 2008 Stephen Soltesz - vsys-0.7-8 +- ignore non-existent directories after restart. +- + +* Fri May 16 2008 Stephen Soltesz - vsys-0.7-7 +- added logrotate configuration to package. +- + +* Mon May 12 2008 Stephen Soltesz - vsys-0.7-6 +- Added two new scripts for CoMon on 4.2 +- + +* Tue May 06 2008 Stephen Soltesz - vsys-0.7-5 +- +- Corrected directory that the script mounts to the correct one: +- /var/local/fprobe +- + +* Wed Apr 23 2008 Stephen Soltesz - vsys-0.7-4 +- Pulling the latest changes for the 4.2rc2 release +- + +* Fri Feb 15 2008 Faiyaz Ahmed - vsys-0.7-2 vsys-0.7-3 +- * daemonization, writing to a logfile, and saving the pid +- + diff --git a/vsyssh/vsyssh.c b/vsyssh/vsyssh.c index 105f565..0239fec 100644 --- a/vsyssh/vsyssh.c +++ b/vsyssh/vsyssh.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -22,47 +23,61 @@ #include #include +void pipe_handler (int sig) { + printf("SIGPIPE"); +} + int main(int argc, char **argv, char **envp) { if (argc<2) { - printf("Usage: vsyssh \n"); + printf("Usage: vsyssh [cmd]\n"); exit(1); } else { int vfd0,vfd1; char *inf,*outf; + struct timeval tv; + + signal(SIGPIPE,pipe_handler); inf=(char *)malloc(strlen(argv[1])+3); - outf=(char *)malloc(strlen(argv[2])+4); + outf=(char *)malloc(strlen(argv[1])+4); strcpy(inf,argv[1]); - strcpy(outf,argv[2]); + strcpy(outf,argv[1]); strcat(inf,".in"); strcat(outf,".out"); + vfd0 = open(outf,O_RDONLY|O_NONBLOCK); + printf("Out file: %d\n",vfd0); vfd1 = open(inf,O_WRONLY); - vfd0 = open(outf,O_RDONLY); + printf("In file: %d\n",vfd1); if (vfd0==-1 || vfd1 == -1) { - printf("Error opening vsys entry %s\n", argv[1]); + printf("Error opening vsys entry %s (%s)\n", argv[1],strerror(errno)); exit(1); } - if (argc<3) { fd_set set; FD_ZERO(&set); - FD_SET(0,&set); - FD_SET(vfd0,&set); - while (1) { + FD_SET(0, &set); + FD_SET(vfd0, &set); + + while (1) + { int ret; - ret = select(2, &set, NULL, NULL, NULL); + printf("vsys>");fflush(stdout); + FD_SET(0, &set); + FD_SET(vfd0, &set); + ret = select(vfd0+1, &set, NULL, NULL, NULL); if (FD_ISSET(0,&set)) { char lineread[2048]; int ret; ret=read(0,lineread,2048); + lineread[ret]='\0'; + printf ("writing %s\n",lineread); write(vfd1,lineread,ret); FD_CLR(0,&set); - } - else if (FD_ISSET(vfd0,&set)) { + } if (FD_ISSET(vfd0,&set)) { char lineread[2048]; int ret; ret=read(vfd0,lineread,2048);