diff --git a/alpine/Dockerfile b/alpine/Dockerfile index 285dc7ab8..d287ea157 100644 --- a/alpine/Dockerfile +++ b/alpine/Dockerfile @@ -23,8 +23,8 @@ COPY mkinitrd.sh /bin/ COPY packages/9pudc/9pudc /sbin/ COPY packages/9pudc/etc /etc/ -COPY packages/9pudfuse/9pudfuse /sbin/ -COPY packages/9pudfuse/etc /etc/ +COPY packages/transfused/transfused /sbin/ +COPY packages/transfused/etc /etc/ COPY packages/mdnstool/mdnstool /sbin/ COPY packages/mdnstool/etc /etc/ COPY packages/docker/docker /usr/bin/ @@ -78,7 +78,7 @@ RUN \ rc-update add docker default && \ rc-update add 9pinit boot && \ rc-update add 9pudc default && \ - rc-update add 9pudfuse default && \ + rc-update add transfused default && \ rc-update add mdnstool default && \ rc-update add automount boot && \ rc-update add diagnostics default && \ diff --git a/alpine/packages/9pudfuse/.gitignore b/alpine/packages/9pudfuse/.gitignore deleted file mode 100644 index a9a5bb2c6..000000000 --- a/alpine/packages/9pudfuse/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/9pudfuse diff --git a/alpine/packages/9pudfuse/.merlin b/alpine/packages/9pudfuse/.merlin deleted file mode 100644 index bde497c73..000000000 --- a/alpine/packages/9pudfuse/.merlin +++ /dev/null @@ -1,6 +0,0 @@ -PKG result -PKG stringext -PKG fd-send-recv - -S . -B . diff --git a/alpine/packages/9pudfuse/Dockerfile b/alpine/packages/9pudfuse/Dockerfile deleted file mode 100644 index 82b0f4c44..000000000 --- a/alpine/packages/9pudfuse/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM ocaml/opam:alpine-3.3_ocaml-4.02.3 - -ENV \ - OPAMYES=true \ - CAML_LD_LIBRARY_PATH="/root/.opam/4.02.3/lib/stublibs:/usr/pkg/lib/ocaml/stublibs" \ - MANPATH="/root/.opam/4.02.3/man:" \ - PERL5LIB="/root/.opam/4.02.3/lib/perl5" \ - OCAML_TOPLEVEL_PATH="/root/.opam/4.02.3/lib/toplevel" \ - PATH=/root/.opam/4.02.3/bin:$PATH - -COPY . . -RUN opam depext -i result stringext fd-send-recv -RUN opam config exec -- ocamlbuild -tag debug -use-ocamlfind -package stringext,fd-send-recv,result fuse_socket_9p.native diff --git a/alpine/packages/9pudfuse/Makefile b/alpine/packages/9pudfuse/Makefile deleted file mode 100644 index 78e5f6990..000000000 --- a/alpine/packages/9pudfuse/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -all: 9pudfuse - -9pudfuse: Dockerfile fuse_socket_9p.ml - docker build -t 9pudfuse:build . - docker run --rm 9pudfuse:build \ - cat fuse_socket_9p.native > 9pudfuse - chmod 755 9pudfuse - -clean: - rm -f 9pudfuse - docker images -q 9pudfuse:build | xargs docker rmi -f diff --git a/alpine/packages/9pudfuse/etc/init.d/9pudfuse b/alpine/packages/9pudfuse/etc/init.d/9pudfuse deleted file mode 100755 index 68bb02a54..000000000 --- a/alpine/packages/9pudfuse/etc/init.d/9pudfuse +++ /dev/null @@ -1,37 +0,0 @@ -#!/sbin/openrc-run - -description="9p socket fuse proxy server" - -depend() -{ - need 9pinit -} - -start() -{ - [ -d /Transfuse ] || exit 0 - - ebegin "Starting FUSE socket passthrough" - - [ -n "${PIDFILE}" ] || PIDFILE=/var/run/9pudfuse.pid - -# start-stop-daemon --start --quiet \ -# --exec /sbin/9pudfuse \ -# --pidfile "${PIDFILE}" \ -# -- -path /Transfuse -# eend $? "Failed to start 9pudfuse" - OCAMLRUNPARAM=b /sbin/9pudfuse & -} - -stop() -{ - [ -d /Transfuse ] || exit 0 - - ebegin "Stopping FUSE socket passthrough" - - [ -n "${PIDFILE}" ] || PIDFILE=/var/run/9pudfuse.pid - - start-stop-daemon --stop --quiet \ - --pidfile "${PIDFILE}" - eend $? "Failed to stop 9pudfuse" -} diff --git a/alpine/packages/9pudfuse/fuse_socket_9p.ml b/alpine/packages/9pudfuse/fuse_socket_9p.ml deleted file mode 100644 index 104ce47f3..000000000 --- a/alpine/packages/9pudfuse/fuse_socket_9p.ml +++ /dev/null @@ -1,170 +0,0 @@ - -exception ReadClosed - -let fusermount = "fusermount" -let path = "/Transfuse" - -module Log = struct - let fatal fmt = - Printf.ksprintf (fun s -> prerr_endline s; exit 1) fmt - let error fmt = - Printf.ksprintf (fun s -> prerr_endline s) fmt - let info fmt = - Printf.ksprintf (fun s -> prerr_endline s) fmt -end - -let events_path = path ^ "/events" - -let try_fork caller_name = - try - Unix.fork () - with Unix.Unix_error (err,"fork",_) -> - Log.fatal "%s fork failed: %s" caller_name (Unix.error_message err) - -let check_status = function - | Unix.WEXITED 0 -> Result.Ok () - | Unix.WEXITED k -> - Result.Error ("exit code "^(string_of_int k)) - | Unix.WSIGNALED k -> - Result.Error ("ocaml kill signal "^(string_of_int k)) - | Unix.WSTOPPED k -> - Result.Error ("ocaml stop signal "^(string_of_int k)) - -let finally f at_end = - let r = try f () with e -> (at_end (); raise e) in - at_end (); - r - -let copy description dst src = - let sz = 1 lsl 16 in - let buf = Bytes.create sz in - (*let pnum = ref 0 in*) - let rec loop () = - let n = Unix.read src buf 0 sz in - (if n = 0 then raise ReadClosed); -(* - let fd = Unix.( - openfile ("/tmp/"^description^"_"^(string_of_int !pnum)) - [O_WRONLY; O_CREAT] 0o600) in - let k = Unix.write fd buf 0 n in - assert (k = n); - Unix.close fd; - incr pnum; -*) - let written = try - Unix.write dst buf 0 n - with - | Unix.Unix_error (Unix.EINVAL, "write", _) -> - failwith ("copy write for "^description^" failed with EINVAL") - | Unix.Unix_error (Unix.ENOENT, "write", _) -> - failwith ("copy write for "^description^" failed with ENOENT") - in - (if n <> written - then Log.error "copy of %s read %d but wrote %d" description n written); - loop () - in - try loop () - with - | ReadClosed -> raise ReadClosed - | e -> (Log.error "copy for %s failed" description; raise e) - -let with_reader id f = - let read_path = Printf.sprintf "%s/connections/%d/read" path id in - let read_fd = Unix.(openfile read_path [O_RDONLY] 0o000) in - try finally (fun () -> - f read_path read_fd - ) (fun () -> - Unix.close read_fd - ) with - | ReadClosed -> exit 0 - -let with_writer id f = - let write_path = Printf.sprintf "%s/connections/%d/write" path id in - let write_fd = Unix.(openfile write_path [O_WRONLY] 0o000) in - finally (fun () -> - f write_path write_fd - ) (fun () -> - Unix.close write_fd; - Unix.unlink write_path - ) - -let read_opts id = with_reader id (fun read_path read_fd -> - let sz = 512 in - let buf = Bytes.create sz in - let n = Unix.read read_fd buf 0 sz in - let opts = Stringext.split ~on:'\000' (Bytes.sub buf 0 n) in - Array.of_list opts -) - -let get_fuse_sock opts = - let wsock, rsock = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in - let wfd = Fd_send_recv.int_of_fd wsock in - let opts = Array.append [|fusermount|] opts in - let pid = Unix.(create_process_env fusermount opts - [|"_FUSE_COMMFD="^(string_of_int wfd)|] - stdin stdout stderr) in - let _, status = Unix.waitpid [] pid in - let () = Unix.(shutdown wsock SHUTDOWN_ALL) in - match check_status status with - | Result.Error str -> - let opts = String.concat " " (Array.to_list opts) in - Log.fatal "%s: %s" opts str - | Result.Ok () -> - (* We must read at least 1 byte, by POSIX! *) - let _, _, fd = Fd_send_recv.recv_fd rsock "\000" 0 1 [] in - let () = Unix.(shutdown rsock SHUTDOWN_ALL) in - fd - -(* readers fork into a new process *) -let start_reader id fuse = - match try_fork "start_reader" with - | 0 -> (* child *) - with_reader id (fun _read_path read_fd -> - copy ("reader_"^string_of_int id) fuse read_fd - ) - | _child_pid -> (* parent *) - () - -(* writers stay in the calling process *) -let start_writer id fuse = with_writer id (fun write_path write_fd -> - copy ("writer_"^string_of_int id) write_fd fuse -) - -let handle_connection id = - Log.info "handle_connection %d" id; - match try_fork "handle_connection" with - | 0 -> (* child *) - let opts = read_opts id in - let fuse = get_fuse_sock opts in - start_reader id fuse; - begin try ignore (start_writer id fuse); exit 0 - with - | e -> prerr_endline (Printexc.to_string e); exit 1 - end - | _child_pid -> (* parent *) - () - -let connection_loop () = - let events = Unix.(openfile events_path [O_RDONLY] 0o000) in - (* 512 bytes is easily big enough to read a whole connection id *) - let sz = 512 in - let buf = Bytes.create sz in - let rec recv () = - begin try - let n = Unix.read events buf 0 sz in - let s = String.trim Bytes.(to_string (sub buf 0 n)) in - let id = int_of_string s in - handle_connection id; - with - | Unix.Unix_error (err,"read",path) -> - Log.fatal "Error reading events file %s: %s" - path (Unix.error_message err) - | Failure "int_of_string" -> - Log.fatal "Failed to parse integer connection id" - end; - recv () - in - recv () - -;; -connection_loop () diff --git a/alpine/packages/Makefile b/alpine/packages/Makefile index 995805a3e..014f78c59 100644 --- a/alpine/packages/Makefile +++ b/alpine/packages/Makefile @@ -1,6 +1,6 @@ all: $(MAKE) -C 9pudc - $(MAKE) -C 9pudfuse + $(MAKE) -C transfused $(MAKE) -C mdnstool $(MAKE) -C docker $(MAKE) -C hupper @@ -8,14 +8,14 @@ all: arm: $(MAKE) -C 9pudc ARCH=arm - $(MAKE) -C 9pudfuse ARCH=arm + $(MAKE) -C transfused ARCH=arm $(MAKE) -C mdnstool ARCH=arm $(MAKE) -C docker ARCH=arm $(MAKE) -C hupper ARCH=arm clean: $(MAKE) -C 9pudc clean - $(MAKE) -C 9pudfuse clean + $(MAKE) -C transfused clean $(MAKE) -C mdnstool clean $(MAKE) -C docker clean $(MAKE) -C hupper clean diff --git a/alpine/packages/diagnostics/diagnostics b/alpine/packages/diagnostics/diagnostics index 5ed2299e6..b81f24733 100755 --- a/alpine/packages/diagnostics/diagnostics +++ b/alpine/packages/diagnostics/diagnostics @@ -11,8 +11,8 @@ UDC=$(ps -eo args | grep '^/sbin/9pudc') [ $? -eq 0 ] && printf "✓ Process 9pudc running: $UDC\n" || printf "✗ No 9pudc process\n" if [ -d /Transfuse ] then - FUSE=$(ps -eo args | grep '^/sbin/9pudfuse') - [ $? -eq 0 ] && printf "✓ Process 9pudfuse running\n" || printf "✗ No 9pudfuse process\n" + FUSE=$(ps -eo args | grep '^/sbin/transfused') + [ $? -eq 0 ] && printf "✓ Process transfused running\n" || printf "✗ No transfused process\n" fi MDNS=$(ps -eo args | grep '^/sbin/mdnstool') [ $? -eq 0 ] && printf "✓ Process mdnstool running: $MDNS\n" || printf "✗ No mdnstool process\n" diff --git a/alpine/packages/docker/etc/init.d/docker b/alpine/packages/docker/etc/init.d/docker index 19231e9c5..90e6e725f 100755 --- a/alpine/packages/docker/etc/init.d/docker +++ b/alpine/packages/docker/etc/init.d/docker @@ -27,6 +27,6 @@ start_stop_daemon_args="--background \ depend() { - after 9pudc 9pudfuse + after 9pudc transfused before chronyd } diff --git a/alpine/packages/hostlog/etc/init.d/hostlog b/alpine/packages/hostlog/etc/init.d/hostlog index b0e19cca5..f773ac8c0 100755 --- a/alpine/packages/hostlog/etc/init.d/hostlog +++ b/alpine/packages/hostlog/etc/init.d/hostlog @@ -3,7 +3,7 @@ depends() { before acpid docker syslog chronyd - after 9pinit 9pudfuse + after 9pinit transfused } start() diff --git a/alpine/packages/transfused/.gitignore b/alpine/packages/transfused/.gitignore new file mode 100644 index 000000000..497a34c5f --- /dev/null +++ b/alpine/packages/transfused/.gitignore @@ -0,0 +1 @@ +/transfused diff --git a/alpine/packages/transfused/Dockerfile b/alpine/packages/transfused/Dockerfile new file mode 100644 index 000000000..d0c3197c0 --- /dev/null +++ b/alpine/packages/transfused/Dockerfile @@ -0,0 +1,10 @@ +FROM alpine:3.3 + +RUN apk update && apk upgrade && apk add alpine-sdk + +RUN mkdir -p /transfused +WORKDIR /transfused + +COPY . /transfused + +RUN make transfused diff --git a/alpine/packages/transfused/Makefile b/alpine/packages/transfused/Makefile new file mode 100644 index 000000000..d5739d5b7 --- /dev/null +++ b/alpine/packages/transfused/Makefile @@ -0,0 +1,15 @@ +.PHONY: all + +DEPS=transfused.c + +all: Dockerfile $(DEPS) + docker build -t transfused:build . + docker run transfused:build cat transfused > transfused + chmod 755 transfused + +transfused: $(DEPS) + gcc -g -static -Wall -Werror -o transfused transfused.c + +clean: + rm -f transfused + docker images -q transfused:build | xargs docker rmi -f diff --git a/alpine/packages/transfused/etc/init.d/transfused b/alpine/packages/transfused/etc/init.d/transfused new file mode 100755 index 000000000..dd8ff212f --- /dev/null +++ b/alpine/packages/transfused/etc/init.d/transfused @@ -0,0 +1,32 @@ +#!/sbin/openrc-run + +description="fuse proxy server" + +depend() +{ + need 9pinit +} + +start() +{ + [ -d /Transfuse ] || exit 0 + + ebegin "Starting FUSE socket passthrough" + + [ -n "${PIDFILE}" ] || PIDFILE=/var/run/transfused.pid + + /sbin/transfused -p "${PIDFILE}" & +} + +stop() +{ + [ -d /Transfuse ] || exit 0 + + ebegin "Stopping FUSE socket passthrough" + + [ -n "${PIDFILE}" ] || PIDFILE=/var/run/transfused.pid + + start-stop-daemon --stop --quiet \ + --pidfile "${PIDFILE}" + eend $? "Failed to stop transfused" +} diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c new file mode 100644 index 000000000..5179d4af1 --- /dev/null +++ b/alpine/packages/transfused/transfused.c @@ -0,0 +1,524 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COPY_BUFSZ 65536 + +#define DEFAULT_FUSERMOUNT "/bin/fusermount" +#define DEFAULT_SOCKET9P_ROOT "/Transfuse" + +char * default_fusermount = DEFAULT_FUSERMOUNT; +char * default_socket9p_root = DEFAULT_SOCKET9P_ROOT; +char * usage = + "usage: transfused [-p pidfile] [-9 socket9p_root] [-f fusermount]\n" + " -p pidfile\tthe path at which to write the pid of the process\n" + " -9 " DEFAULT_SOCKET9P_ROOT "\tthe root of the 9p socket file system\n" + " -f " DEFAULT_FUSERMOUNT "\tthe fusermount executable to use\n"; + +int debug = 0; + +typedef struct { + char * socket9p_root; + char * fusermount; + char * pidfile; +} parameters; + +typedef struct { + parameters * params; + long id; +} connection_state; + +typedef struct { + char * descr; + long connection; + char * tag; + int from; + int to; +} copy_thread_state; + +void die(int exit_code, const char * perror_arg, const char * fmt, ...) { + va_list argp; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + if (perror_arg != NULL) perror(perror_arg); + exit(exit_code); +} + +void * must_malloc(char *const descr, size_t size) { + void * ptr; + + ptr = malloc(size); + if (size != 0 && ptr == NULL) die(1, descr, ""); + + return ptr; +} + +char ** read_opts(connection_state * connection, char * buf) { + int read_fd; + char * read_path; + int read_count; + int optc = 1; + char ** optv; + + if (asprintf(&read_path, "%s/connections/%ld/read", + connection->params->socket9p_root, connection->id) == -1) + die(1, "Couldn't allocate read path", ""); + + read_fd = open(read_path, O_RDONLY); + if (read_fd == -1) + die(1, "couldn't open read path", "For connection %ld, ", connection->id); + + read_count = read(read_fd, buf, COPY_BUFSZ - 1); + if (read_count == -1) die(1, "read_opts error reading", ""); + + buf[read_count] = 0x0; + + for (int i = 0; i < read_count; i++) { + if (buf[i] == 0x0) optc++; + } + + optv = (char **) must_malloc("read_opts optv", (optc + 1) * sizeof(void *)); + optv[0] = buf; + optv[optc] = 0x0; + + int j = 1; + for (int i = 0; i < read_count && j < optc; i++) { + if (buf[i] == 0x0) { + optv[j] = buf + i + 1; + j++; + } + } + + free(read_path); + + return optv; +} + +uint64_t message_id(uint64_t * message) { + return message[1]; +} + +void copy(copy_thread_state * copy_state) { + int from = copy_state->from; + int to = copy_state->to; + char * descr = copy_state->descr; + int read_count, write_count; + long connection = copy_state->connection; + char * tag = copy_state->tag; + void * buf; + + buf = must_malloc(descr, COPY_BUFSZ); + + while(1) { + read_count = read(from, buf, COPY_BUFSZ); + if (read_count == -1) die(1, "", "copy %s: error reading: ", descr); + + if (debug) { + int trace_fd; + char * trace_path; + + if (asprintf(&trace_path, "/tmp/transfused.%ld.%s.%llu", + connection, tag, message_id(buf)) == -1) + die(1, "Couldn't allocate trace packet path", ""); + + trace_fd = open(trace_path, O_WRONLY | O_CREAT, 0600); + if (trace_fd == -1) + die(1, "couldn't open trace packet path", "For %s, ", descr); + + write_count = write(trace_fd, buf, read_count); + if (write_count == -1) + die(1, "", "copy %s trace: error writing %s: ", descr, trace_path); + + if (write_count != read_count) + die(1, NULL, "copy %s trace: read %d but only write %d\n", + descr, read_count, write_count); + + close(trace_fd); + free(trace_path); + } + + write_count = write(to, buf, read_count); + if (write_count == -1) die(1, "", "copy %s: error writing: ", descr); + + if (write_count != read_count) + die(1, NULL, "copy %s: read %d by only wrote %d\n", + descr, read_count, write_count); + } + + free(buf); +} + +void * copy_clean_from(copy_thread_state * copy_state) { + copy(copy_state); + + close(copy_state->from); + + free(copy_state->descr); + free(copy_state); + + return NULL; +} + +void * copy_clean_from_thread(void * copy_state) { + return (copy_clean_from((copy_thread_state *) copy_state)); +} + +void * copy_clean_to(copy_thread_state * copy_state) { + copy(copy_state); + + close(copy_state->to); + + free(copy_state->descr); + free(copy_state); + + return NULL; +} + +int recv_fd(int sock) { + int ret; + int fd = -1; + char iochar; + char buf[CMSG_SPACE(sizeof(fd))]; + + struct msghdr msg; + struct iovec vec; + struct cmsghdr *cmsg; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + vec.iov_base = &iochar; + vec.iov_len = 1; + msg.msg_iov = &vec; + + msg.msg_iovlen = 1; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + ret = recvmsg(sock, &msg, 0); + + if (ret == -1) die(1, "recvmsg", ""); + + if (ret > 0 && msg.msg_controllen > 0) { + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg->cmsg_level == SOL_SOCKET && (cmsg->cmsg_type == SCM_RIGHTS)) { + fd = *(int*)CMSG_DATA(cmsg); + } + } + + return fd; +} + +// optv must be null-terminated +int get_fuse_sock(char * fusermount, char *const optv[]) { + int optc; + char ** argv; + char * envp[2]; + pid_t fusermount_pid; + int fuse_socks[2]; + int status; + int fd; + + // prepare argv from optv + for (optc = 0; optv[optc] != NULL; optc++) {} + + argv = (char **) must_malloc("fusermount argv", (optc + 2) * sizeof(char *)); + + argv[0] = fusermount; + memcpy(&argv[1], optv, (optc + 1) * sizeof(char *)); + + // make the socket over which we'll be sent the FUSE socket fd + if (socketpair(PF_UNIX, SOCK_STREAM, 0, fuse_socks)) + die(1, "Couldn't create FUSE socketpair", ""); + + // prepare to exec the suid binary fusermount + if (asprintf(&envp[0], "_FUSE_COMMFD=%d", fuse_socks[0]) == -1) + die(1, "Couldn't allocate fusermount envp", ""); + + envp[1] = 0x0; + + // fork and exec fusermount + fusermount_pid = fork(); + if (!fusermount_pid) // child + if (execve(fusermount, argv, envp)) + die(1, "Failed to execute fusermount", ""); + + // parent + free(argv); + free(envp[0]); + + // close the end of the socket that we gave away + close(fuse_socks[0]); + + // wait for fusermount to return + waitpid(fusermount_pid, &status, 0); + if (!WIFEXITED(status)) + die(1, NULL, "fusermount terminated abnormally\n"); + + if (WEXITSTATUS(status)) + die(1, NULL, "fusermount exited with code %d\n", WEXITSTATUS(status)); + + if (debug) fprintf(stderr, "about to recv_fd from fusermount\n"); + + fd = recv_fd(fuse_socks[1]); + if (fd == -1) + die(1, NULL, "Couldn't receive fd over FUSE socket\n"); + + // close the read end of the socket + close(fuse_socks[1]); + + return fd; +} + +void start_reader(connection_state * connection, int fuse) { + int read_fd; + char * read_path; + pthread_t child; + copy_thread_state * copy_state; + + if (asprintf(&read_path, "%s/connections/%ld/read", + connection->params->socket9p_root, connection->id) == -1) + die(1, "Couldn't allocate read path", ""); + + read_fd = open(read_path, O_RDONLY); + if (read_fd == -1) + die(1, "couldn't open read path", "For connection %ld, ", connection->id); + + copy_state = (copy_thread_state *) must_malloc("start_reader copy_state", + sizeof(copy_thread_state)); + copy_state->descr = read_path; + copy_state->connection = connection->id; + copy_state->tag = "read"; + copy_state->from = read_fd; + copy_state->to = fuse; + if ((errno = pthread_create(&child, NULL, + copy_clean_from_thread, copy_state))) + die(1, "", "couldn't create read copy thread for connection %ld: ", + connection->id); + + if ((errno = pthread_detach(child))) + die (1, "", "couldn't detach read copy thread for connection '%ld': ", + connection->id); +} + +void do_write(connection_state * connection, int fuse) { + int write_fd; + char * write_path; + copy_thread_state * copy_state; + + if (asprintf(&write_path, "%s/connections/%ld/write", + connection->params->socket9p_root, connection->id) == -1) + die(1, "Couldn't allocate write path", ""); + + write_fd = open(write_path, O_WRONLY); + if (write_fd == -1) + die(1, "couldn't open write path", "For connection %ld, ", connection->id); + + copy_state = (copy_thread_state *) must_malloc("do_write copy_state", + sizeof(copy_thread_state)); + copy_state->descr = write_path; + copy_state->connection = connection->id; + copy_state->tag = "write"; + copy_state->from = fuse; + copy_state->to = write_fd; + copy_clean_to(copy_state); +} + +void * handle_connection(connection_state * connection) { + char ** optv; + int fuse; + char * buf; + + buf = (char *) must_malloc("read_opts packet malloc", COPY_BUFSZ); + + optv = read_opts(connection, buf); + fuse = get_fuse_sock(connection->params->fusermount, optv); + free(optv); + free(buf); + + start_reader(connection, fuse); + do_write(connection, fuse); + free(connection); + + return NULL; +} + +void * handle_connection_thread(void * connection) { + return handle_connection((connection_state *) connection); +} + +void toggle_debug(int sig) { + debug = !debug; +} + +void setup_debug() { + if (SIG_ERR == signal(SIGHUP, toggle_debug)) + die(1, "Couldn't set SIGHUP behavior", ""); + + if (siginterrupt(SIGHUP, 1)) + die(1, "Couldn't set siginterrupt for SIGHUP", ""); +} + +void parse_parameters(int argc, char * argv[], parameters * params) { + int c; + int errflg = 0; + + params->pidfile = NULL; + params->socket9p_root = NULL; + params->fusermount = NULL; + + while ((c = getopt(argc, argv, ":p:9:f:")) != -1) { + switch(c) { + + case 'p': + params->pidfile = optarg; + break; + + case '9': + params->socket9p_root = optarg; + break; + + case 'f': + params->fusermount = optarg; + break; + + case ':': + fprintf(stderr, "Option -%c requires a path argument\n", optopt); + errflg++; + break; + + case '?': + fprintf(stderr, "Unrecognized option: '-%c'\n", optopt); + errflg++; + break; + + default: + fprintf(stderr, "Internal error parsing -%c\n", c); + errflg++; + } + } + + if (errflg) die(2, NULL, usage); + + if (params->pidfile != NULL && access(params->pidfile, W_OK)) + if (errno != ENOENT) + die(2, "", "-p %s path to pidfile must be writable: ", params->pidfile); + + if (params->fusermount == NULL) + params->fusermount = default_fusermount; + if (access(params->fusermount, X_OK)) + die(2, "", "-f %s path to fusermount must be executable: ", + params->fusermount); + + if (params->socket9p_root == NULL) + params->socket9p_root = default_socket9p_root; + if (access(params->socket9p_root, X_OK)) + die(2, "", "-9 %s path to socket 9p root directory must be executable: ", + params->socket9p_root); +} + +void write_pidfile(char * pidfile) { + int fd; + pid_t pid = getpid(); + char * pid_s; + int pid_s_len, write_count; + + if (asprintf(&pid_s, "%lld", (long long) pid) == -1) + die(1, "Couldn't allocate pidfile string", ""); + + pid_s_len = strlen(pid_s); + + fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) + die(1, "", "Couldn't open pidfile path %s: ", pidfile); + + write_count = write(fd, pid_s, pid_s_len); + if (write_count == -1) + die(1, "", "Error writing pidfile %s: ", pidfile); + + if (write_count != pid_s_len) + die(1, NULL, "Error writing %s to pidfile %s: only wrote %d bytes\n", + pid_s, pidfile, write_count); + + close(fd); + free(pid_s); +} + +#define ID_LEN 512 + +void process_events(char * events_path, int events, parameters * params) { + char buf[ID_LEN]; + int read_count; + long conn_id; + pthread_t child; + connection_state * conn; + + while (1) { + read_count = read(events, buf, ID_LEN - 1); + if (read_count == -1) { + die(1, "", "Error reading events path %s: ", events_path); + } else if (read_count == 0) { + // TODO: this is probably the 9p server's fault due to + // not dropping the read 0 to force short read if + // the real read is flushed + fprintf(stderr, "read 0 from event stream %s\n", events_path); + continue; + } + + buf[read_count] = 0x0; + + errno = 0; + conn_id = strtol(buf, NULL, 10); + if (errno) die(1, "failed", "Connection id of string '%s'", buf); + + if (debug) fprintf(stderr, "handle connection %ld\n", conn_id); + + conn = (connection_state *) must_malloc("connection state", + sizeof(connection_state)); + conn->id = conn_id; + conn->params = params; + + if ((errno = pthread_create(&child, NULL, + handle_connection_thread, conn))) + die(1, "", "Couldn't create thread for connection '%ld': ", conn_id); + + if ((errno = pthread_detach(child))) + die(1, "", "Couldn't detach thread for connection '%ld': ", conn_id); + + if (debug) fprintf(stderr, "thread spawned\n"); + } +} + +int main(int argc, char * argv[]) { + int events; + parameters params; + char * events_path; + + parse_parameters(argc, argv, ¶ms); + setup_debug(); + + if (params.pidfile != NULL) write_pidfile(params.pidfile); + + if (asprintf(&events_path, "%s/events", params.socket9p_root) == -1) + die(1, "Couldn't allocate events path", ""); + + events = open(events_path, O_RDONLY | O_CLOEXEC); + if (events != -1) process_events(events_path, events, ¶ms); + + fprintf(stderr, "Failed to open events path %s: ", events_path); + perror(""); + free(events_path); + return 1; +} diff --git a/xhyve/fuserun.sh b/xhyve/fuserun.sh index 3e57f2309..60742f031 100755 --- a/xhyve/fuserun.sh +++ b/xhyve/fuserun.sh @@ -17,7 +17,7 @@ fi #IMG_HDD="-s 4,virtio-blk,/somepath/somefile.img" PCI_DEV="-s 0:0,hostbridge -s 31,lpc" RND="-s 5,virtio-rnd" -FS="-s 6,virtio-9p,path=9pudfuse.sock,tag=fuse" +FS="-s 6,virtio-9p,path=transfused.sock,tag=fuse" LPC_DEV="-l com1,stdio" ACPI="-A" CLOCK="-u"