diff options
Diffstat (limited to 'src/dumm')
-rw-r--r-- | src/dumm/Makefile.am | 12 | ||||
-rw-r--r-- | src/dumm/Makefile.in | 538 | ||||
-rw-r--r-- | src/dumm/bridge.c | 171 | ||||
-rw-r--r-- | src/dumm/bridge.h | 76 | ||||
-rw-r--r-- | src/dumm/cowfs.c | 913 | ||||
-rw-r--r-- | src/dumm/cowfs.h | 54 | ||||
-rw-r--r-- | src/dumm/dumm.c | 391 | ||||
-rw-r--r-- | src/dumm/dumm.h | 95 | ||||
-rw-r--r-- | src/dumm/guest.c | 567 | ||||
-rw-r--r-- | src/dumm/guest.h | 155 | ||||
-rw-r--r-- | src/dumm/iface.c | 179 | ||||
-rw-r--r-- | src/dumm/iface.h | 79 | ||||
-rw-r--r-- | src/dumm/main.c | 632 | ||||
-rw-r--r-- | src/dumm/mconsole.c | 349 | ||||
-rw-r--r-- | src/dumm/mconsole.h | 71 |
15 files changed, 4282 insertions, 0 deletions
diff --git a/src/dumm/Makefile.am b/src/dumm/Makefile.am new file mode 100644 index 000000000..3356e7a57 --- /dev/null +++ b/src/dumm/Makefile.am @@ -0,0 +1,12 @@ +lib_LTLIBRARIES = libdumm.la +ipsec_PROGRAMS = dumm + +libdumm_la_SOURCES = dumm.c dumm.h guest.c guest.h iface.c iface.h bridge.c bridge.h mconsole.c mconsole.h cowfs.h cowfs.c +dumm_SOURCES = main.c + +libdumm_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lpthread -lbridge -lfuse -lutil ${xml_LIBS} +dumm_LDADD = -ldumm -lreadline + +INCLUDES = -I$(top_srcdir)/src/libstrongswan ${xml_CFLAGS} + +AM_CFLAGS = -D_FILE_OFFSET_BITS=64 diff --git a/src/dumm/Makefile.in b/src/dumm/Makefile.in new file mode 100644 index 000000000..94ad6003a --- /dev/null +++ b/src/dumm/Makefile.in @@ -0,0 +1,538 @@ +# Makefile.in generated by automake 1.10 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +ipsec_PROGRAMS = dumm$(EXEEXT) +subdir = src/dumm +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(ipsecdir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libdumm_la_DEPENDENCIES = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(am__DEPENDENCIES_1) +am_libdumm_la_OBJECTS = dumm.lo guest.lo iface.lo bridge.lo \ + mconsole.lo cowfs.lo +libdumm_la_OBJECTS = $(am_libdumm_la_OBJECTS) +ipsecPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(ipsec_PROGRAMS) +am_dumm_OBJECTS = main.$(OBJEXT) +dumm_OBJECTS = $(am_dumm_OBJECTS) +dumm_DEPENDENCIES = +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libdumm_la_SOURCES) $(dumm_SOURCES) +DIST_SOURCES = $(libdumm_la_SOURCES) $(dumm_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPSEC_ROUTING_TABLE = @IPSEC_ROUTING_TABLE@ +IPSEC_ROUTING_TABLE_PRIO = @IPSEC_ROUTING_TABLE_PRIO@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LINUX_HEADERS = @LINUX_HEADERS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +backenddir = @backenddir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dbus_CFLAGS = @dbus_CFLAGS@ +dbus_LIBS = @dbus_LIBS@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +interfacedir = @interfacedir@ +ipsecdir = @ipsecdir@ +ipsecgid = @ipsecgid@ +ipsecuid = @ipsecuid@ +libdir = @libdir@ +libexecdir = @libexecdir@ +linuxdir = @linuxdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +xml_CFLAGS = @xml_CFLAGS@ +xml_LIBS = @xml_LIBS@ +lib_LTLIBRARIES = libdumm.la +libdumm_la_SOURCES = dumm.c dumm.h guest.c guest.h iface.c iface.h bridge.c bridge.h mconsole.c mconsole.h cowfs.h cowfs.c +dumm_SOURCES = main.c +libdumm_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lpthread -lbridge -lfuse -lutil ${xml_LIBS} +dumm_LDADD = -ldumm -lreadline +INCLUDES = -I$(top_srcdir)/src/libstrongswan ${xml_CFLAGS} +AM_CFLAGS = -D_FILE_OFFSET_BITS=64 +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/dumm/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/dumm/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libdumm.la: $(libdumm_la_OBJECTS) $(libdumm_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libdumm_la_OBJECTS) $(libdumm_la_LIBADD) $(LIBS) +install-ipsecPROGRAMS: $(ipsec_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(ipsecdir)" || $(MKDIR_P) "$(DESTDIR)$(ipsecdir)" + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(ipsecdir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(ipsecdir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-ipsecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(ipsecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(ipsecdir)/$$f"; \ + done + +clean-ipsecPROGRAMS: + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +dumm$(EXEEXT): $(dumm_OBJECTS) $(dumm_DEPENDENCIES) + @rm -f dumm$(EXEEXT) + $(LINK) $(dumm_OBJECTS) $(dumm_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bridge.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cowfs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dumm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/guest.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iface.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mconsole.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(ipsecdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-ipsecPROGRAMS clean-libLTLIBRARIES \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-ipsecPROGRAMS + +install-dvi: install-dvi-am + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-ipsecPROGRAMS uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-ipsecPROGRAMS clean-libLTLIBRARIES clean-libtool ctags \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-ipsecPROGRAMS install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-ipsecPROGRAMS uninstall-libLTLIBRARIES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/dumm/bridge.c b/src/dumm/bridge.c new file mode 100644 index 000000000..c6068e60c --- /dev/null +++ b/src/dumm/bridge.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <sys/types.h> +#include <libbridge.h> + +#include <debug.h> +#include <utils/linked_list.h> + +#include "bridge.h" + +typedef struct private_bridge_t private_bridge_t; + +struct private_bridge_t { + /** public interface */ + bridge_t public; + /** device name */ + char *name; + /** list of attached interfaces */ + linked_list_t *ifaces; +}; + +/** + * Implementation of bridge_t.get_name. + */ +static char* get_name(private_bridge_t *this) +{ + return this->name; +} + +/** + * Implementation of bridge_t.create_iface_iterator. + */ +static iterator_t* create_iface_iterator(private_bridge_t *this) +{ + return this->ifaces->create_iterator(this->ifaces, TRUE); +} + +/** + * Implementation of bridge_t.disconnect_iface. + */ +static bool disconnect_iface(private_bridge_t *this, iface_t *iface) +{ + iterator_t *iterator; + iface_t *current; + bool good = FALSE; + + iterator = this->ifaces->create_iterator(this->ifaces, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current == iface) + { + if (br_del_interface(this->name, iface->get_hostif(iface)) != 0) + { + DBG1("removing iface '%s' from bridge '%s' in kernel failed: %m", + iface->get_hostif(iface), this->name); + } + else + { + iface->set_bridge(iface, NULL); + good = TRUE; + } + break; + } + } + if (iface != current) + { + DBG1("iface '%s' not found on bridge '%s'", iface->get_hostif(iface), + this->name); + } + iterator->destroy(iterator); + return good; +} + +/** + * Implementation of bridge_t.connect_iface. + */ +static bool connect_iface(private_bridge_t *this, iface_t *iface) +{ + if (br_add_interface(this->name, iface->get_hostif(iface)) != 0) + { + DBG1("adding iface '%s' to bridge '%s' failed: %m", + iface->get_hostif(iface), this->name); + return FALSE; + } + iface->set_bridge(iface, &this->public); + this->ifaces->insert_last(this->ifaces, iface); + return TRUE; +} + +/** + * instance counter to (de-)initialize libbridge + */ +static int instances = 0; + +/** + * unregister an interface from bridge + */ +static void unregister(iface_t *iface) +{ + iface->set_bridge(iface, NULL); +} + +/** + * Implementation of bridge_t.destroy. + */ +static void destroy(private_bridge_t *this) +{ + this->ifaces->invoke_function(this->ifaces, (void(*)(void*))unregister); + this->ifaces->destroy(this->ifaces); + if (br_del_bridge(this->name) != 0) + { + DBG1("deleting bridge '%s' from kernel failed: %m", this->name); + } + free(this->name); + free(this); + if (--instances == 0) + { + br_shutdown(); + } +} + +/** + * create the bridge instance + */ +bridge_t *bridge_create(char *name) +{ + private_bridge_t *this; + + if (instances == 0) + { + if (br_init() != 0) + { + DBG1("libbridge initialization failed: %m"); + return NULL; + } + } + + this = malloc_thing(private_bridge_t); + this->public.get_name = (char*(*)(bridge_t*))get_name; + this->public.create_iface_iterator = (iterator_t*(*)(bridge_t*))create_iface_iterator; + this->public.disconnect_iface = (bool(*)(bridge_t*, iface_t *iface))disconnect_iface; + this->public.connect_iface = (bool(*)(bridge_t*, iface_t *iface))connect_iface; + this->public.destroy = (void*)destroy; + + if (br_add_bridge(name) != 0) + { + DBG1("creating bridge '%s' failed: %m", name); + free(this); + return NULL; + } + + this->name = strdup(name); + this->ifaces = linked_list_create(); + + instances++; + return &this->public; +} + diff --git a/src/dumm/bridge.h b/src/dumm/bridge.h new file mode 100644 index 000000000..6d28ed376 --- /dev/null +++ b/src/dumm/bridge.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef BRIDGE_H +#define BRIDGE_H + +#include <library.h> +#include <utils/iterator.h> + +typedef struct bridge_t bridge_t; + +#include "iface.h" + +/** + * @brief Interface in a guest, connected to a tap device on the host. + */ +struct bridge_t { + + /** + * @brief Get the name of the bridge. + * + * @return name of the bridge + */ + char* (*get_name)(bridge_t *this); + + /** + * @brief Add an interface to a bridge. + * + * @param iface interface to add + * @return TRUE if interface added + */ + bool (*connect_iface)(bridge_t *this, iface_t *iface); + + /** + * @brief Remove an interface from a bridge. + * + * @param iface interface to remove + * @return TRUE if interface removed + */ + bool (*disconnect_iface)(bridge_t *this, iface_t *iface); + + /** + * @brief Create an iterator over all interfaces. + * + * @return iterator over iface_t's + */ + iterator_t* (*create_iface_iterator)(bridge_t *this); + + /** + * @brief Destroy a bridge + */ + void (*destroy) (bridge_t *this); +}; + +/** + * @brief Create a new bridge. + * + * @param name name of the bridge to create + * @return bridge, NULL if failed + */ +bridge_t *bridge_create(char *name); + +#endif /* BRIDGE_H */ + diff --git a/src/dumm/cowfs.c b/src/dumm/cowfs.c new file mode 100644 index 000000000..4c16c7c5d --- /dev/null +++ b/src/dumm/cowfs.c @@ -0,0 +1,913 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2001-2007 Miklos Szeredi + * + * Based on example shipped with FUSE. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#define FUSE_USE_VERSION 26 +#define _GNU_SOURCE + +#include <fuse.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <sys/time.h> +#include <pthread.h> + +#include "cowfs.h" + +#include <library.h> +#include <debug.h> + +/** define _XOPEN_SOURCE 500 fails when using libstrongswan, define popen */ +extern ssize_t pread(int fd, void *buf, size_t count, off_t offset); +extern ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); + +typedef struct private_cowfs_t private_cowfs_t; + +struct private_cowfs_t { + /** public cowfs interface */ + cowfs_t public; + /** fuse channel to mountpoint */ + struct fuse_chan *chan; + /** fuse handle */ + struct fuse *fuse; + /** mountpoint of cowfs FUSE */ + char *mount; + /** master filesystem path */ + char *master; + /** host filesystem path */ + char *host; + /** overlay filesystem path */ + char *over; + /** fd of read only master filesystem */ + int master_fd; + /** copy on write overlay to master */ + int host_fd; + /** optional COW overlay */ + int over_fd; + /** thread processing FUSE */ + pthread_t thread; +}; + +/** + * get this pointer stored in fuse context + */ +static private_cowfs_t *get_this() +{ + return (fuse_get_context())->private_data; +} + +/** + * make a path relative + */ +static void rel(const char **path) +{ + if (**path == '/') + { + (*path)++; + } + if (**path == '\0') + { + *path = "."; + } +} + +/** + * get the highest overlay in which path exists + */ +static int get_rd(const char *path) +{ + private_cowfs_t *this = get_this(); + + if (this->over_fd > 0 && faccessat(this->over_fd, path, F_OK, 0) == 0) + { + return this->over_fd; + } + if (faccessat(this->host_fd, path, F_OK, 0) == 0) + { + return this->host_fd; + } + return this->master_fd; +} + +/** + * get the highest overlay available, to write something + */ +static int get_wr(const char *path) +{ + private_cowfs_t *this = get_this(); + if (this->over_fd > 0) + { + return this->over_fd; + } + return this->host_fd; +} + +/** + * create full "path" at "wr" the same way they exist at "rd" + */ +static bool clone_path(int rd, int wr, const char *path) +{ + char *pos, *full; + struct stat st; + full = strdupa(path); + pos = full; + + while ((pos = strchr(pos, '/'))) + { + *pos = '\0'; + if (fstatat(wr, full, &st, 0) < 0) + { + /* TODO: handle symlinks!? */ + if (fstatat(rd, full, &st, 0) < 0) + { + return FALSE; + } + if (mkdirat(wr, full, st.st_mode) < 0) + { + return FALSE; + } + } + *pos = '/'; + pos++; + } + return TRUE; +} + +/** + * copy a (special) file from a readonly to a read-write overlay + */ +static int copy(const char *path) +{ + char *buf[4096]; + int len; + int rd, wr; + int from, to; + struct stat st; + + rd = get_rd(path); + wr = get_wr(path); + + if (rd == wr) + { + /* already writeable */ + return wr; + } + if (fstatat(rd, path, &st, 0) < 0) + { + return -1; + } + if (!clone_path(rd, wr, path)) + { + return -1; + } + if (mknodat(wr, path, st.st_mode, st.st_rdev) < 0) + { + return -1; + } + /* copy if no special file */ + if (st.st_size) + { + from = openat(rd, path, O_RDONLY, st.st_mode); + if (from < 0) + { + return -1; + } + to = openat(wr, path, O_WRONLY , st.st_mode); + if (to < 0) + { + close(from); + return -1; + } + while ((len = read(from, buf, sizeof(buf))) > 0) + { + if (write(to, buf, len) < len) + { + /* TODO: only on len < 0 ? */ + close(from); + close(to); + return -1; + } + } + close(from); + close(to); + if (len < 0) + { + return -1; + } + } + return wr; +} + +/** + * FUSE getattr method + */ +static int cowfs_getattr(const char *path, struct stat *stbuf) +{ + rel(&path); + + if (fstatat(get_rd(path), path, stbuf, AT_SYMLINK_NOFOLLOW) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE access method + */ +static int cowfs_access(const char *path, int mask) +{ + rel(&path); + + if (faccessat(get_rd(path), path, mask, 0) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE readlink method + */ +static int cowfs_readlink(const char *path, char *buf, size_t size) +{ + int res; + + rel(&path); + + res = readlinkat(get_rd(path), path, buf, size - 1); + if (res < 0) + { + return -errno; + } + buf[res] = '\0'; + return 0; +} + +/** + * get a directory stream of two concatenated paths + */ +static DIR* get_dir(char *dir, const char *subdir) +{ + char *full; + + if (dir == NULL) + { + return NULL; + } + + full = alloca(strlen(dir) + strlen(subdir) + 1); + strcpy(full, dir); + strcat(full, subdir); + + return opendir(full); +} + +/** + * check if a directory stream contains a directory + */ +static bool contains_dir(DIR *d, char *dirname) +{ + if (d) + { + struct dirent *ent; + + rewinddir(d); + while ((ent = readdir(d))) + { + if (streq(ent->d_name, dirname)) + { + return TRUE; + } + } + } + return FALSE; +} + +/** + * FUSE readdir method + */ +static int cowfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + private_cowfs_t *this = get_this(); + DIR *d1, *d2, *d3; + struct stat st; + struct dirent *ent; + + memset(&st, 0, sizeof(st)); + + d1 = get_dir(this->master, path); + d2 = get_dir(this->host, path); + d3 = get_dir(this->over, path); + + if (d1) + { + while ((ent = readdir(d1))) + { + if (!contains_dir(d2, ent->d_name) && + !contains_dir(d3, ent->d_name)) + { + st.st_ino = ent->d_ino; + st.st_mode = ent->d_type << 12; + filler(buf, ent->d_name, &st, 0); + } + } + closedir(d1); + } + if (d2) + { + rewinddir(d2); + while ((ent = readdir(d2))) + { + if (!contains_dir(d3, ent->d_name)) + { + st.st_ino = ent->d_ino; + st.st_mode = ent->d_type << 12; + filler(buf, ent->d_name, &st, 0); + } + } + closedir(d2); + } + if (d3) + { + rewinddir(d3); + while ((ent = readdir(d3))) + { + st.st_ino = ent->d_ino; + st.st_mode = ent->d_type << 12; + filler(buf, ent->d_name, &st, 0); + } + closedir(d3); + } + return 0; +} + +/** + * FUSE mknod method + */ +static int cowfs_mknod(const char *path, mode_t mode, dev_t rdev) +{ + int fd; + rel(&path); + + fd = get_wr(path); + if (!clone_path(get_rd(path), fd, path)) + { + return -errno; + } + + if (mknodat(fd, path, mode, rdev) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE mkdir method + */ +static int cowfs_mkdir(const char *path, mode_t mode) +{ + int fd; + rel(&path); + + fd = get_wr(path); + if (!clone_path(get_rd(path), fd, path)) + { + return -errno; + } + if (mkdirat(fd, path, mode) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE unlink method + */ +static int cowfs_unlink(const char *path) +{ + rel(&path); + + /* TODO: whiteout master */ + if (unlinkat(get_wr(path), path, 0) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE rmdir method + */ +static int cowfs_rmdir(const char *path) +{ + rel(&path); + + /* TODO: whiteout master */ + if (unlinkat(get_wr(path), path, AT_REMOVEDIR) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE symlink method + */ +static int cowfs_symlink(const char *from, const char *to) +{ + int fd; + const char *fromrel = from; + + rel(&to); + rel(&fromrel); + + fd = get_wr(to); + if (!clone_path(get_rd(fromrel), fd, fromrel)) + { + return -errno; + } + if (symlinkat(from, fd, to) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE rename method + */ +static int cowfs_rename(const char *from, const char *to) +{ + int fd; + private_cowfs_t *this = get_this(); + + rel(&from); + rel(&to); + + fd = get_rd(from); + if (fd == this->master_fd) + { + fd = copy(from); + if (fd < 0) + { + return -errno; + } + } + + if (renameat(fd, from, get_wr(to), to) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE link method + */ +static int cowfs_link(const char *from, const char *to) +{ + int rd, wr; + + rel(&from); + rel(&to); + + rd = get_rd(from); + wr = get_wr(to); + + if (!clone_path(rd, wr, to)) + { + DBG1("cloning path '%s' failed", to); + return -errno; + } + if (linkat(rd, from, wr, to, 0) < 0) + { + DBG1("linking '%s' to '%s' failed", from, to); + return -errno; + } + return 0; +} + +/** + * FUSE chmod method + */ +static int cowfs_chmod(const char *path, mode_t mode) +{ + int fd; + struct stat st; + private_cowfs_t *this = get_this(); + + rel(&path); + fd = get_rd(path); + if (fd == this->master_fd) + { + if (fstatat(fd, path, &st, 0) < 0) + { + return -errno; + } + if (st.st_mode == mode) + { + return 0; + } + fd = copy(path); + if (fd < 0) + { + return -errno; + } + } + if (fchmodat(fd, path, mode, 0) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE chown method + */ +static int cowfs_chown(const char *path, uid_t uid, gid_t gid) +{ + int fd; + struct stat st; + private_cowfs_t *this = get_this(); + + rel(&path); + fd = get_rd(path); + if (fd == this->master_fd) + { + if (fstatat(fd, path, &st, 0) < 0) + { + return -errno; + } + if (st.st_uid == uid && st.st_gid == gid) + { + return 0; + } + fd = copy(path); + if (fd < 0) + { + return -errno; + } + } + if (fchownat(fd, path, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE truncate method + */ +static int cowfs_truncate(const char *path, off_t size) +{ + int fd; + struct stat st; + + private_cowfs_t *this = get_this(); + + rel(&path); + fd = get_rd(path); + if (fd == this->master_fd) + { + if (fstatat(fd, path, &st, 0) < 0) + { + return -errno; + } + if (st.st_size == size) + { + return 0; + } + fd = copy(path); + if (fd < 0) + { + return -errno; + } + } + fd = openat(fd, path, O_WRONLY); + if (fd < 0) + { + return -errno; + } + if (ftruncate(fd, size) < 0) + { + close(fd); + return -errno; + } + close(fd); + return 0; +} + +/** + * FUSE utimens method + */ +static int cowfs_utimens(const char *path, const struct timespec ts[2]) +{ + struct timeval tv[2]; + int fd; + private_cowfs_t *this = get_this(); + + rel(&path); + fd = get_rd(path); + if (fd == this->master_fd) + { + fd = copy(path); + if (fd < 0) + { + return -errno; + } + } + + tv[0].tv_sec = ts[0].tv_sec; + tv[0].tv_usec = ts[0].tv_nsec / 1000; + tv[1].tv_sec = ts[1].tv_sec; + tv[1].tv_usec = ts[1].tv_nsec / 1000; + + if (futimesat(fd, path, tv) < 0) + { + return -errno; + } + return 0; +} + +/** + * FUSE open method + */ +static int cowfs_open(const char *path, struct fuse_file_info *fi) +{ + int fd; + + rel(&path); + fd = get_rd(path); + + fd = openat(fd, path, fi->flags); + if (fd < 0) + { + return -errno; + } + close(fd); + return 0; +} + +/** + * FUSE read method + */ +static int cowfs_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + int file, fd, res; + + rel(&path); + + fd = get_rd(path); + + file = openat(fd, path, O_RDONLY); + if (file < 0) + { + return -errno; + } + + res = pread(file, buf, size, offset); + if (res < 0) + { + res = -errno; + } + close(file); + return res; +} + +/** + * FUSE write method + */ +static int cowfs_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + private_cowfs_t *this = get_this(); + int file, fd, res; + + rel(&path); + + fd = get_rd(path); + if (fd == this->master_fd) + { + fd = copy(path); + if (fd < 0) + { + return -errno; + } + } + file = openat(fd, path, O_WRONLY); + if (file < 0) + { + return -errno; + } + res = pwrite(file, buf, size, offset); + if (res < 0) + { + res = -errno; + } + close(file); + return res; +} + +/** + * FUSE statfs method + */ +static int cowfs_statfs(const char *path, struct statvfs *stbuf) +{ + private_cowfs_t *this = get_this(); + int fd; + + fd = this->host_fd; + if (this->over_fd > 0) + { + fd = this->over_fd; + } + + if (fstatvfs(fd, stbuf) < 0) + { + return -errno; + } + + return 0; +} + +/** + * FUSE init method + */ +static void *cowfs_init(struct fuse_conn_info *conn) +{ + struct fuse_context *ctx; + + ctx = fuse_get_context(); + + return ctx->private_data; +} + +/** + * FUSE method vectors + */ +static struct fuse_operations cowfs_operations = { + .getattr = cowfs_getattr, + .access = cowfs_access, + .readlink = cowfs_readlink, + .readdir = cowfs_readdir, + .mknod = cowfs_mknod, + .mkdir = cowfs_mkdir, + .symlink = cowfs_symlink, + .unlink = cowfs_unlink, + .rmdir = cowfs_rmdir, + .rename = cowfs_rename, + .link = cowfs_link, + .chmod = cowfs_chmod, + .chown = cowfs_chown, + .truncate = cowfs_truncate, + .utimens = cowfs_utimens, + .open = cowfs_open, + .read = cowfs_read, + .write = cowfs_write, + .statfs = cowfs_statfs, + .init = cowfs_init, +}; + +/** + * Implementation of cowfs_t.set_overlay. + */ +static bool set_overlay(private_cowfs_t *this, char *path) +{ + if (this->over) + { + free(this->over); + this->over = NULL; + } + if (this->over_fd > 0) + { + close(this->over_fd); + this->over_fd = -1; + } + if (path) + { + this->over_fd = open(path, O_RDONLY | O_DIRECTORY); + if (this->over_fd < 0) + { + DBG1("failed to open overlay directory '%s': %m", path); + return FALSE; + } + this->over = strdup(path); + } + return TRUE; +} + +/** + * stop, umount and destroy a cowfs FUSE filesystem + */ +static void destroy(private_cowfs_t *this) +{ + fuse_exit(this->fuse); + fuse_unmount(this->mount, this->chan); + pthread_join(this->thread, NULL); + fuse_destroy(this->fuse); + free(this->mount); + free(this->master); + free(this->host); + free(this->over); + close(this->master_fd); + close(this->host_fd); + if (this->over_fd > 0) + { + close(this->over_fd); + } + free(this); +} + +/** + * creates a new cowfs fuse instance + */ +cowfs_t *cowfs_create(char *master, char *host, char *mount) +{ + struct fuse_args args = {0, NULL, 0}; + private_cowfs_t *this = malloc_thing(private_cowfs_t); + + this->public.set_overlay = (bool(*)(cowfs_t*, char *path))set_overlay; + this->public.destroy = (void(*)(cowfs_t*))destroy; + + this->master_fd = open(master, O_RDONLY | O_DIRECTORY); + if (this->master_fd < 0) + { + DBG1("failed to open master filesystem '%s'", master); + free(this); + } + this->host_fd = open(host, O_RDONLY | O_DIRECTORY); + if (this->master_fd < 0) + { + DBG1("failed to open host filesystem '%s'", host); + close(this->master_fd); + free(this); + } + this->over_fd = -1; + + this->chan = fuse_mount(mount, &args); + if (this->chan == NULL) + { + DBG1("mounting cowfs FUSE on '%s' failed", mount); + close(this->master_fd); + close(this->host_fd); + free(this); + return NULL; + } + + this->fuse = fuse_new(this->chan, &args, &cowfs_operations, + sizeof(cowfs_operations), this); + if (this->fuse == NULL) + { + DBG1("creating cowfs FUSE handle failed"); + close(this->master_fd); + close(this->host_fd); + fuse_unmount(mount, this->chan); + free(this); + return NULL; + } + + this->mount = strdup(mount); + this->master = strdup(master); + this->host = strdup(host); + this->over = NULL; + + if (pthread_create(&this->thread, NULL, (void*)fuse_loop, this->fuse) != 0) + { + DBG1("creating thread to handle FUSE failed"); + fuse_unmount(mount, this->chan); + free(this->mount); + free(this->master); + free(this->host); + close(this->master_fd); + close(this->host_fd); + free(this); + return NULL; + } + + return &this->public; +} + diff --git a/src/dumm/cowfs.h b/src/dumm/cowfs.h new file mode 100644 index 000000000..419197dd6 --- /dev/null +++ b/src/dumm/cowfs.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef COWFS_H +#define COWFS_H + +#include <library.h> + +typedef struct cowfs_t cowfs_t; + +/** + * @brief cowfs - Copy on write FUSE filesystem. + * + */ +struct cowfs_t { + + /** + * @brief Set an additional copy on write overlay. + * + * @param path path of the overlay + * @return FALSE if failed + */ + bool (*set_overlay)(cowfs_t *this, char *path); + + /** + * @brief Stop, umount and destroy a cowfs FUSE filesystem. + */ + void (*destroy) (cowfs_t *this); +}; + +/** + * @brief Mount a cowfs FUSE filesystem. + * + * @param master read only master file system directory + * @param host copy on write host directory + * @param mount mountpoint where union is mounted + * @return instance, or NULL if FUSE initalization failed + */ +cowfs_t *cowfs_create(char *master, char *host, char *mount); + +#endif /* COWFS_H */ + diff --git a/src/dumm/dumm.c b/src/dumm/dumm.c new file mode 100644 index 000000000..b9a2814e6 --- /dev/null +++ b/src/dumm/dumm.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> + +#include <debug.h> + +#include "dumm.h" + +#define PERME (S_IRWXU | S_IRWXG) +#define GUEST_DIR "guests" +#define TEMPLATE_DIR "templates" +#define TEMPLATE_DIR_DIR "diff" + +/** + * instances of dumm, used to deliver signals + */ +static linked_list_t *instances = NULL; + +typedef struct private_dumm_t private_dumm_t; + +struct private_dumm_t { + /** public dumm interface */ + dumm_t public; + /** working dir */ + char *dir; + /** directory of guests */ + char *guest_dir; + /** directory of templates */ + char *template_dir; + /** directory of loaded template */ + char *template; + /** list of managed guests */ + linked_list_t *guests; + /** list of managed bridges */ + linked_list_t *bridges; + /** do not catch signals if we are destroying */ + bool destroying; +}; + +/** + * Implementation of dumm_t.create_guest. + */ +static guest_t* create_guest(private_dumm_t *this, char *name, char *kernel, + char *master, int mem) +{ + guest_t *guest; + + guest = guest_create(this->guest_dir, name, kernel, master, mem); + if (guest) + { + this->guests->insert_last(this->guests, guest); + } + return guest; +} + +/** + * Implementation of dumm_t.create_guest_iterator. + */ +static iterator_t* create_guest_iterator(private_dumm_t *this) +{ + return this->guests->create_iterator(this->guests, TRUE); +} + +/** + * Implementation of dumm_t.create_bridge. + */ +static bridge_t* create_bridge(private_dumm_t *this, char *name) +{ + bridge_t *bridge; + + bridge = bridge_create(name); + if (bridge) + { + this->bridges->insert_last(this->bridges, bridge); + } + return bridge; +} + +/** + * Implementation of dumm_t.create_bridge_iterator. + */ +static iterator_t* create_bridge_iterator(private_dumm_t *this) +{ + return this->bridges->create_iterator(this->bridges, TRUE); +} + +/** + * disable the currently enabled template + */ +static void clear_template(private_dumm_t *this) +{ + iterator_t *iterator, *ifaces; + guest_t *guest; + iface_t *iface; + + free(this->template); + this->template = NULL; + + iterator = this->guests->create_iterator(this->guests, TRUE); + while (iterator->iterate(iterator, (void**)&guest)) + { + guest->load_template(guest, NULL); + ifaces = guest->create_iface_iterator(guest); + while (ifaces->iterate(ifaces, (void**)&iface)) + { + ifaces->remove(ifaces); + iface->destroy(iface); + } + ifaces->destroy(ifaces); + } + iterator->destroy(iterator); +} + +/** + * Implementation of dumm_t.load_template. + */ +static bool load_template(private_dumm_t *this, char *name) +{ + iterator_t *iterator; + guest_t *guest; + char dir[PATH_MAX]; + size_t len; + + clear_template(this); + + if (name == NULL) + { + return TRUE; + } + + free(this->template); + asprintf(&this->template, "%s/%s", this->template_dir, name); + len = snprintf(dir, sizeof(dir), "%s/%s", this->template, TEMPLATE_DIR_DIR); + if (len < 0 || len >= sizeof(dir)) + { + return FALSE; + } + + if (access(this->template, F_OK) != 0) + { /* does not exist, create template */ + if (mkdir(this->template, PERME) != 0) + { + DBG1("creating template directory '%s' failed: %m", this->template); + return FALSE; + } + if (mkdir(dir, PERME) != 0) + { + DBG1("creating template overlay directory '%s' failed: %m", dir); + return FALSE; + } + } + iterator = this->guests->create_iterator(this->guests, TRUE); + while (iterator->iterate(iterator, (void**)&guest)) + { + if (!guest->load_template(guest, dir)) + { + iterator->destroy(iterator); + clear_template(this); + return FALSE; + } + } + iterator->destroy(iterator); + return TRUE; +} + +/** + * signal handler + */ +void signal_handler(int sig, siginfo_t *info, void *ucontext) +{ + if (sig == SIGCHLD) + { + switch (info->si_code) + { + case CLD_EXITED: + case CLD_KILLED: + case CLD_DUMPED: + { + private_dumm_t *this; + guest_t *guest; + iterator_t *iterator, *guests; + + iterator = instances->create_iterator(instances, TRUE); + while (iterator->iterate(iterator, (void**)&this)) + { + if (this->destroying) + { + continue; + } + guests = this->guests->create_iterator(this->guests, TRUE); + while (guests->iterate(guests, (void**)&guest)) + { + if (guest->get_pid(guest) == info->si_pid) + { + guest->sigchild(guest); + break; + } + } + guests->destroy(guests); + } + iterator->destroy(iterator); + break; + } + default: + break; + } + + } + /* SIGHUP is currently just ignored */ +} + +/** + * add a dumm instance + */ +static void add_instance(private_dumm_t *this) +{ + if (instances == NULL) + { + struct sigaction action; + + instances = linked_list_create(); + + memset(&action, 0, sizeof(action)); + action.sa_sigaction = signal_handler; + action.sa_flags = SA_SIGINFO; + + if (sigaction(SIGCHLD, &action, NULL) != 0 || + sigaction(SIGHUP, &action, NULL) != 0) + { + DBG1("installing signal handler failed!"); + } + } + instances->insert_last(instances, this); +} + +/** + * remove a dumm instance + */ +static void remove_instance(private_dumm_t *this) +{ + iterator_t *iterator; + private_dumm_t *current; + + iterator = instances->create_iterator(instances, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current == this) + { + iterator->remove(iterator); + break; + } + } + iterator->destroy(iterator); + if (instances->get_count(instances) == 0) + { + instances->destroy(instances); + instances = NULL; + } +} + +/** + * Implementation of dumm_t.destroy + */ +static void destroy(private_dumm_t *this) +{ + iterator_t *iterator; + guest_t *guest; + + this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy)); + + iterator = this->guests->create_iterator(this->guests, TRUE); + while (iterator->iterate(iterator, (void**)&guest)) + { + guest->stop(guest); + } + iterator->destroy(iterator); + + this->destroying = TRUE; + this->guests->destroy_offset(this->guests, offsetof(guest_t, destroy)); + free(this->guest_dir); + free(this->template_dir); + free(this->template); + free(this->dir); + remove_instance(this); + free(this); +} + +/** + * load all guests in our working dir + */ +static void load_guests(private_dumm_t *this) +{ + DIR *dir; + struct dirent *ent; + guest_t *guest; + + dir = opendir(this->guest_dir); + if (dir == NULL) + { + return; + } + + while ((ent = readdir(dir))) + { + if (streq(ent->d_name, ".") || streq(ent->d_name, "..")) + { + continue; + } + guest = guest_load(this->guest_dir, ent->d_name); + if (guest) + { + DBG1("loaded guest '%s'", ent->d_name); + this->guests->insert_last(this->guests, guest); + } + else + { + DBG1("loading guest in directory '%s' failed, skipped", ent->d_name); + } + } + closedir(dir); +} + +/** + * create a dumm instance + */ +dumm_t *dumm_create(char *dir) +{ + char cwd[PATH_MAX]; + private_dumm_t *this = malloc_thing(private_dumm_t); + + this->public.create_guest = (guest_t*(*)(dumm_t*,char*,char*,char*,int))create_guest; + this->public.create_guest_iterator = (iterator_t*(*)(dumm_t*))create_guest_iterator; + this->public.create_bridge = (bridge_t*(*)(dumm_t*, char *name))create_bridge; + this->public.create_bridge_iterator = (iterator_t*(*)(dumm_t*))create_bridge_iterator; + this->public.load_template = (bool(*)(dumm_t*, char *name))load_template; + this->public.destroy = (void(*)(dumm_t*))destroy; + + this->destroying = FALSE; + if (*dir == '/' || getcwd(cwd, sizeof(cwd)) == 0) + { + this->dir = strdup(dir); + } + else + { + asprintf(&this->dir, "%s/%s", cwd, dir); + } + this->template = NULL; + asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR); + asprintf(&this->template_dir, "%s/%s", this->dir, TEMPLATE_DIR); + this->guests = linked_list_create(); + this->bridges = linked_list_create(); + + add_instance(this); + + if (mkdir(this->guest_dir, PERME) < 0 && errno != EEXIST) + { + DBG1("creating guest directory '%s' failed: %m", this->guest_dir); + destroy(this); + return NULL; + } + if (mkdir(this->template_dir, PERME) < 0 && errno != EEXIST) + { + DBG1("creating template directory '%s' failed: %m", this->template_dir); + destroy(this); + return NULL; + } + + load_guests(this); + return &this->public; +} + diff --git a/src/dumm/dumm.h b/src/dumm/dumm.h new file mode 100644 index 000000000..5414f9993 --- /dev/null +++ b/src/dumm/dumm.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef DUMM_H +#define DUMM_H + +#include <signal.h> + +#include <library.h> +#include <utils/linked_list.h> + +#include "guest.h" +#include "bridge.h" + +typedef struct dumm_t dumm_t; + +/** + * @brief dumm - Dynamic Uml Mesh Modeler + * + * Controls a group of UML guests and their networks. + * Dumm catches SIGCHD and SIGHUP to trace UML child processes and the FUSE + * filesystem. Do not overwrite these signal handlers! + */ +struct dumm_t { + + /** + * @brief Starts a new UML guest + * + * @param name name of the guest + * @param kernel UML kernel to use for guest + * @param master mounted read only master filesystem + * @param mem amount of memory for guest, in MB + * @return guest if started, NULL if failed + */ + guest_t* (*create_guest) (dumm_t *this, char *name, char *kernel, + char *master, int mem); + + /** + * @brief Create an iterator over all guests. + * + * @return iteraotor over guest_t's + */ + iterator_t* (*create_guest_iterator) (dumm_t *this); + + /** + * @brief Create a new bridge. + * + * @param name name of the bridge to create + * @return created bridge + */ + bridge_t* (*create_bridge)(dumm_t *this, char *name); + + /** + * @brief Create an iterator over all bridges. + * + * @return iterator over bridge_t's + */ + iterator_t* (*create_bridge_iterator)(dumm_t *this); + + /** + * @brief Loads a template, create a new one if it does not exist. + * + * @param name name of the template, NULL to close + * @return FALSE if load/create failed + */ + bool (*load_template)(dumm_t *this, char *name); + + /** + * @brief stop all guests and destroy the modeler + */ + void (*destroy) (dumm_t *this); +}; + +/** + * @brief Create a group of UML hosts and networks. + * + * @param dir directory to create guests/load from + * @return created UML group, or NULL if failed. + */ +dumm_t *dumm_create(char *dir); + +#endif /* DUMM_H */ + diff --git a/src/dumm/guest.c b/src/dumm/guest.c new file mode 100644 index 000000000..bbb59f431 --- /dev/null +++ b/src/dumm/guest.c @@ -0,0 +1,567 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/uio.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <signal.h> +#include <dirent.h> +#include <termios.h> + +#include <debug.h> +#include <utils/linked_list.h> + +#include "dumm.h" +#include "guest.h" +#include "mconsole.h" +#include "cowfs.h" + +#define PERME (S_IRWXU | S_IRWXG) +#define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) + +#define MASTER_DIR "master" +#define DIFF_DIR "diff" +#define UNION_DIR "union" +#define MEMORY_FILE "mem" +#define KERNEL_FILE "linux" +#define LOG_FILE "boot.log" +#define NOTIFY_FILE "notify" +#define PTYS 0 + +typedef struct private_guest_t private_guest_t; + +struct private_guest_t { + /** implemented public interface */ + guest_t public; + /** name of the guest */ + char *name; + /** directory of guest */ + int dir; + /** directory name of guest */ + char *dirname; + /** amount of memory for guest, in MB */ + int mem; + /** pid of guest child process */ + int pid; + /** state of guest */ + guest_state_t state; + /** log file for console 0 */ + int bootlog; + /** FUSE cowfs instance */ + cowfs_t *cowfs; + /** mconsole to control running UML */ + mconsole_t *mconsole; + /** list of interfaces attached to the guest */ + linked_list_t *ifaces; +}; + +ENUM(guest_state_names, GUEST_STOPPED, GUEST_STOPPING, + "STOPPED", + "STARTING", + "RUNNING", + "PAUSED", + "STOPPING", +); + +/** + * Implementation of guest_t.get_name. + */ +static char* get_name(private_guest_t *this) +{ + return this->name; +} + +/** + * Implementation of guest_t.create_iface. + */ +static iface_t* create_iface(private_guest_t *this, char *name) +{ + iterator_t *iterator; + iface_t *iface; + + if (this->state != GUEST_RUNNING) + { + DBG1("guest '%s' not running, unable to add interface", this->name); + return NULL; + } + + iterator = this->ifaces->create_iterator(this->ifaces, TRUE); + while (iterator->iterate(iterator, (void**)&iface)) + { + if (streq(name, iface->get_guestif(iface))) + { + DBG1("guest '%s' already has an interface '%s'", this->name, name); + iterator->destroy(iterator); + return NULL; + } + } + iterator->destroy(iterator); + + iface = iface_create(this->name, name, this->mconsole); + if (iface) + { + this->ifaces->insert_last(this->ifaces, iface); + } + return iface; +} + +/** + * Implementation of guest_t.create_iface_iterator. + */ +static iterator_t* create_iface_iterator(private_guest_t *this) +{ + return this->ifaces->create_iterator(this->ifaces, TRUE); +} + +/** + * Implementation of guest_t.get_state. + */ +static guest_state_t get_state(private_guest_t *this) +{ + return this->state; +} + +/** + * Implementation of guest_t.get_pid. + */ +static pid_t get_pid(private_guest_t *this) +{ + return this->pid; +} + +/** + * write format string to a buffer, and advance buffer position + */ +static char* write_arg(char **pos, size_t *left, char *format, ...) +{ + size_t len; + char *res = NULL; + va_list args; + + va_start(args, format); + len = vsnprintf(*pos, *left, format, args); + va_end(args); + if (len < *left) + { + res = *pos; + len++; + *pos += len + 1; + *left -= len + 1; + } + return res; +} + +/** + * Implementation of get_t.close_console. + */ +static char* get_console(private_guest_t *this, int console) +{ + if (this->state == GUEST_RUNNING) + { + return this->mconsole->get_console_pts(this->mconsole, console); + } + return NULL; +} + +/** + * Implementation of guest_t.stop. + */ +static void stop(private_guest_t *this) +{ + if (this->state != GUEST_STOPPED) + { + this->state = GUEST_STOPPING; + this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy)); + this->ifaces = linked_list_create(); + kill(this->pid, SIGINT); + waitpid(this->pid, NULL, 0); + this->state = GUEST_STOPPED; + } +} + +/** + * Implementation of guest_t.start. + */ +static bool start(private_guest_t *this) +{ + char buf[2048]; + char *notify; + char *pos = buf; + char *args[16]; + int i = 0; + size_t left = sizeof(buf); + + if (this->state != GUEST_STOPPED) + { + DBG1("unable to start guest in state %N", guest_state_names, this->state); + return FALSE; + } + this->state = GUEST_STARTING; + + notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE); + + args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE); + args[i++] = write_arg(&pos, &left, "root=/dev/root"); + args[i++] = write_arg(&pos, &left, "rootfstype=hostfs"); + args[i++] = write_arg(&pos, &left, "rootflags=%s/%s", this->dirname, UNION_DIR); + args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname); + args[i++] = write_arg(&pos, &left, "umid=%s", this->name); + args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem); + args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify); + args[i++] = write_arg(&pos, &left, "con=pts"); + args[i++] = write_arg(&pos, &left, "con0=none,fd:%d", this->bootlog); + args[i++] = NULL; + + this->pid = fork(); + switch (this->pid) + { + case 0: /* child, */ + dup2(open("/dev/null", 0), 0); + dup2(this->bootlog, 1); + dup2(this->bootlog, 2); + execvp(args[0], args); + DBG1("starting UML kernel '%s' failed: %m", args[0]); + exit(1); + case -1: + this->state = GUEST_STOPPED; + return FALSE; + default: + break; + } + /* open mconsole */ + this->mconsole = mconsole_create(notify); + if (this->mconsole == NULL) + { + DBG1("opening mconsole at '%s' failed, stopping guest", buf); + stop(this); + return FALSE; + } + + this->state = GUEST_RUNNING; + return TRUE; +} + +/** + * Implementation of guest_t.load_template. + */ +static bool load_template(private_guest_t *this, char *path) +{ + char dir[PATH_MAX]; + size_t len; + + if (path == NULL) + { + return this->cowfs->set_overlay(this->cowfs, NULL); + } + + len = snprintf(dir, sizeof(dir), "%s/%s", path, this->name); + if (len < 0 || len >= sizeof(dir)) + { + return FALSE; + } + if (access(dir, F_OK) != 0) + { + if (mkdir(dir, PERME) != 0) + { + DBG1("creating overlay for guest '%s' failed: %m", this->name); + return FALSE; + } + } + return this->cowfs->set_overlay(this->cowfs, dir); +} + +/** + * Implementation of guest_t.sigchild. + */ +static void sigchild(private_guest_t *this) +{ + if (this->state != GUEST_STOPPING) + { /* collect zombie if uml crashed */ + waitpid(this->pid, NULL, WNOHANG); + } + DESTROY_IF(this->mconsole); + this->mconsole = NULL; + this->state = GUEST_STOPPED; +} + +/** + * umount the union filesystem + */ +static bool umount_unionfs(private_guest_t *this) +{ + if (this->cowfs) + { + this->cowfs->destroy(this->cowfs); + this->cowfs = NULL; + return TRUE; + } + return FALSE; +} + +/** + * mount the union filesystem + */ +static bool mount_unionfs(private_guest_t *this) +{ + char master[PATH_MAX]; + char diff[PATH_MAX]; + char mount[PATH_MAX]; + + if (this->cowfs == NULL) + { + snprintf(master, sizeof(master), "%s/%s", this->dirname, MASTER_DIR); + snprintf(diff, sizeof(diff), "%s/%s", this->dirname, DIFF_DIR); + snprintf(mount, sizeof(mount), "%s/%s", this->dirname, UNION_DIR); + + this->cowfs = cowfs_create(master, diff, mount); + if (this->cowfs) + { + return TRUE; + } + } + return FALSE; +} + +/** + * open logfile for boot messages + */ +static int open_bootlog(private_guest_t *this) +{ + int fd; + + fd = openat(this->dir, LOG_FILE, O_WRONLY | O_CREAT, PERM); + if (fd == -1) + { + DBG1("opening bootlog failed, using stdout"); + return 1; + } + return fd; +} + +/** + * load memory configuration from file + */ +int loadmem(private_guest_t *this) +{ + FILE *file; + int mem = 0; + + file = fdopen(openat(this->dir, MEMORY_FILE, O_RDONLY, PERM), "r"); + if (file) + { + if (fscanf(file, "%d", &mem) <= 0) + { + mem = 0; + } + fclose(file); + } + return mem; +} + +/** + * save memory configuration to file + */ +bool savemem(private_guest_t *this, int mem) +{ + FILE *file; + bool retval = FALSE; + + file = fdopen(openat(this->dir, MEMORY_FILE, O_RDWR | O_CREAT | O_TRUNC, + PERM), "w"); + if (file) + { + if (fprintf(file, "%d", mem) > 0) + { + retval = TRUE; + } + fclose(file); + } + return retval; +} + +/** + * Implementation of guest_t.destroy. + */ +static void destroy(private_guest_t *this) +{ + stop(this); + umount_unionfs(this); + if (this->bootlog > 1) + { + close(this->bootlog); + } + if (this->dir > 0) + { + close(this->dir); + } + free(this->dirname); + free(this->name); + free(this); +} + +/** + * generic guest constructor + */ +static private_guest_t *guest_create_generic(char *parent, char *name, + bool create) +{ + char cwd[PATH_MAX]; + private_guest_t *this = malloc_thing(private_guest_t); + + this->public.get_name = (void*)get_name; + this->public.get_pid = (pid_t(*)(guest_t*))get_pid; + this->public.get_state = (guest_state_t(*)(guest_t*))get_state; + this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface; + this->public.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator; + this->public.start = (void*)start; + this->public.stop = (void*)stop; + this->public.get_console = (char*(*)(guest_t*,int))get_console; + this->public.load_template = (bool(*)(guest_t*, char *path))load_template; + this->public.sigchild = (void(*)(guest_t*))sigchild; + this->public.destroy = (void*)destroy; + + if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL) + { + asprintf(&this->dirname, "%s/%s", parent, name); + } + else + { + asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name); + } + if (create) + { + mkdir(this->dirname, PERME); + } + this->dir = open(this->dirname, O_DIRECTORY, PERME); + if (this->dir < 0) + { + DBG1("opening guest directory '%s' failed: %m", this->dirname); + free(this->dirname); + free(this); + return NULL; + } + + this->pid = 0; + this->state = GUEST_STOPPED; + this->mconsole = NULL; + this->ifaces = linked_list_create(); + this->mem = 0; + this->bootlog = open_bootlog(this); + this->name = strdup(name); + this->cowfs = NULL; + + return this; +} + +/** + * create a symlink to old called new in our working dir + */ +static bool make_symlink(private_guest_t *this, char *old, char *new) +{ + char cwd[PATH_MAX]; + char buf[PATH_MAX]; + + if (*old == '/' || getcwd(cwd, sizeof(cwd)) == NULL) + { + snprintf(buf, sizeof(buf), "%s", old); + } + else + { + snprintf(buf, sizeof(buf), "%s/%s", cwd, old); + } + return symlinkat(buf, this->dir, new) == 0; +} + + +/** + * create the guest instance, including required dirs and mounts + */ +guest_t *guest_create(char *parent, char *name, char *kernel, + char *master, int mem) +{ + private_guest_t *this = guest_create_generic(parent, name, TRUE); + + if (this == NULL) + { + return NULL; + } + + if (!make_symlink(this, master, MASTER_DIR) || + !make_symlink(this, kernel, KERNEL_FILE)) + { + DBG1("creating master/kernel symlink failed: %m"); + destroy(this); + return NULL; + } + + if (mkdirat(this->dir, UNION_DIR, PERME) != 0 || + mkdirat(this->dir, DIFF_DIR, PERME) != 0) + { + DBG1("unable to create directories for '%s': %m", name); + destroy(this); + return NULL; + } + + this->mem = mem; + if (!savemem(this, mem)) + { + destroy(this); + return NULL; + } + + if (!mount_unionfs(this)) + { + destroy(this); + return NULL; + } + + return &this->public; +} + +/** + * load an already created guest + */ +guest_t *guest_load(char *parent, char *name) +{ + private_guest_t *this = guest_create_generic(parent, name, FALSE); + + if (this == NULL) + { + return NULL; + } + + this->mem = loadmem(this); + if (this->mem == 0) + { + DBG1("unable to open memory configuration file: %m", name); + destroy(this); + return NULL; + } + + if (!mount_unionfs(this)) + { + destroy(this); + return NULL; + } + + return &this->public; +} + diff --git a/src/dumm/guest.h b/src/dumm/guest.h new file mode 100644 index 000000000..10b37aaa7 --- /dev/null +++ b/src/dumm/guest.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef GUEST_H +#define GUEST_H + +#include <library.h> +#include <utils/iterator.h> + +#include "iface.h" + +typedef enum guest_state_t guest_state_t; + +/** + * @brief State of a guest (started, stopped, ...) + */ +enum guest_state_t { + /** guest kernel not running at all */ + GUEST_STOPPED, + /** kernel started, but not yet available */ + GUEST_STARTING, + /** guest is up and running */ + GUEST_RUNNING, + /** guest has been paused */ + GUEST_PAUSED, + /** guest is stopping (shutting down) */ + GUEST_STOPPING, +}; + +/** + * string mappings for guest_state_t + */ +extern enum_name_t *guest_state_names; + +typedef struct guest_t guest_t; + +/** + * @brief A guest is a UML instance running on the host. + **/ +struct guest_t { + + /** + * @brief Get the name of this guest. + * + * @return name of the guest + */ + char* (*get_name) (guest_t *this); + + /** + * @brief Get the process ID of the guest child process. + * + * @return name of the guest + */ + pid_t (*get_pid) (guest_t *this); + + /** + * @brief Get the state of the guest (stopped, started, etc.). + * + * @return guests state + */ + guest_state_t (*get_state)(guest_t *this); + + /** + * @brief Start the guest. + * + * @return TRUE if guest successfully started + */ + bool (*start) (guest_t *this); + + /** + * @brief Kill the guest. + * + * @return TRUE if guest was running and killed + */ + bool (*stop) (guest_t *this); + + /** + * @brief Get a console pts device. + * + * Every guest has 5 consoles, numbered from 1 to 5. These are associated + * to a unique pts device on the host. + * + * @param console console number to get (1-5) + * @return pts device file name, NULL if failed + */ + char* (*get_console) (guest_t *this, int console); + + /** + * @brief Create a new interface in the current scenario. + * + * @param name name of the interface in the guest + * @return created interface, or NULL if failed + */ + iface_t* (*create_iface)(guest_t *this, char *name); + + /** + * @brief Create an iterator over all guest interfaces. + * + * @return iterator over iface_t's + */ + iterator_t* (*create_iface_iterator)(guest_t *this); + + /** + * @brief Set the template COWFS overlay to use. + * + * @param parent parent directory where template diff should point to + * @return FALSE if failed + */ + bool (*load_template)(guest_t *this, char *parent); + + /** + * @brief Called whenever a SIGCHILD for the guests PID is received. + */ + void (*sigchild)(guest_t *this); + + /** + * @brief Close and destroy a guest with all interfaces + */ + void (*destroy) (guest_t *this); +}; + +/** + * @brief Create a new, unstarted guest. + * + * @param parent parent directory to create the guest in + * @param name name of the guest to create + * @param kernel kernel this guest uses + * @param master read-only master filesystem for guest + * @param mem amount of memory to give the guest + */ +guest_t *guest_create(char *parent, char *name, char *kernel, + char *master, int mem); + +/** + * @brief Load a guest created with guest_create(). + * + * @param parent parent directory to look for a guest + * @param name name of the guest directory + */ +guest_t *guest_load(char *parent, char *name); + +#endif /* GUEST_H */ + diff --git a/src/dumm/iface.c b/src/dumm/iface.c new file mode 100644 index 000000000..3c1bfc470 --- /dev/null +++ b/src/dumm/iface.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2002 Jeff Dike + * + * Based on the "tunctl" utility from Jeff Dike. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <linux/if_tun.h> + +#include <debug.h> + +#include "iface.h" + +typedef struct private_iface_t private_iface_t; + +struct private_iface_t { + /** public interface */ + iface_t public; + /** device name in guest (eth0) */ + char *guestif; + /** device name at host (tap0) */ + char *hostif; + /** bridge this interface is attached to */ + bridge_t *bridge; + /** mconsole for guest */ + mconsole_t *mconsole; +}; + +/** + * Implementation of iface_t.get_guestif. + */ +static char* get_guestif(private_iface_t *this) +{ + return this->guestif; +} + +/** + * Implementation of iface_t.get_hostif. + */ +static char* get_hostif(private_iface_t *this) +{ + return this->hostif; +} + +/** + * Implementation of iface_t.set_bridge. + */ +static void set_bridge(private_iface_t *this, bridge_t *bridge) +{ + this->bridge = bridge; +} + +/** + * destroy the tap device + */ +static bool destroy_tap(private_iface_t *this) +{ + struct ifreq ifr; + int tap; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, this->hostif, sizeof(ifr.ifr_name) - 1); + + tap = open(TAP_DEVICE, O_RDWR); + if (tap < 0) + { + DBG1("unable to open tap device %s: %m", TAP_DEVICE); + return FALSE; + } + if (ioctl(tap, TUNSETIFF, &ifr) < 0 || + ioctl(tap, TUNSETPERSIST, 0) < 0) + { + DBG1("removing %s failed: %m", this->hostif); + close(tap); + return FALSE; + } + close(tap); + return TRUE; +} + +/** + * create the tap device + */ +static char* create_tap(private_iface_t *this, char *guest) +{ + struct ifreq ifr; + int tap; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s-%s", guest, this->guestif); + + tap = open(TAP_DEVICE, O_RDWR); + if (tap < 0) + { + DBG1("unable to open tap device %s: %m", TAP_DEVICE); + return NULL; + } + if (ioctl(tap, TUNSETIFF, &ifr) < 0 || + ioctl(tap, TUNSETPERSIST, 1) < 0 || + ioctl(tap, TUNSETOWNER, 0)) + { + DBG1("creating new tap device failed: %m"); + close(tap); + return NULL; + } + close(tap); + return strdup(ifr.ifr_name); +} + +/** + * Implementation of iface_t.destroy. + */ +static void destroy(private_iface_t *this) +{ + if (this->bridge) + { + this->bridge->disconnect_iface(this->bridge, &this->public); + } + this->mconsole->del_iface(this->mconsole, this->guestif); + destroy_tap(this); + free(this->guestif); + free(this->hostif); + free(this); +} + +/** + * create the iface instance + */ +iface_t *iface_create(char *guest, char *guestif, mconsole_t *mconsole) +{ + private_iface_t *this = malloc_thing(private_iface_t); + + this->public.get_hostif = (char*(*)(iface_t*))get_hostif; + this->public.get_guestif = (char*(*)(iface_t*))get_guestif; + this->public.set_bridge = (void(*)(iface_t*, bridge_t*))set_bridge; + this->public.destroy = (void*)destroy; + + this->mconsole = mconsole; + this->guestif = strdup(guestif); + this->hostif = create_tap(this, guest); + this->bridge = NULL; + if (this->hostif == NULL) + { + destroy_tap(this); + free(this->guestif); + free(this); + return NULL; + } + if (!this->mconsole->add_iface(this->mconsole, this->guestif, this->hostif)) + { + DBG1("creating interface '%s' in guest failed", this->guestif); + destroy_tap(this); + free(this->guestif); + free(this->hostif); + free(this); + return NULL; + } + return &this->public; +} + diff --git a/src/dumm/iface.h b/src/dumm/iface.h new file mode 100644 index 000000000..59de99f22 --- /dev/null +++ b/src/dumm/iface.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IFACE_H +#define IFACE_H + +#include <library.h> +#include <utils/iterator.h> + +#define TAP_DEVICE "/dev/net/tun" + +typedef struct iface_t iface_t; + +#include "mconsole.h" +#include "bridge.h" + +/** + * @brief Interface in a guest, connected to a tap device on the host. + */ +struct iface_t { + + /** + * @brief Get the interface name in the guest (e.g. eth0). + * + * @return guest interface name + */ + char* (*get_guestif)(iface_t *this); + + /** + * @brief Get the interface name at the host (e.g. tap0). + * + * @return host interface (tap device) name + */ + char* (*get_hostif)(iface_t *this); + + /** + * @brief Set the bridge this interface is attached to. + * + * @param bridge assigned bridge, or NULL for none + */ + void (*set_bridge)(iface_t *this, bridge_t *bridge); + + /* + bool (*up) (iface_t *this); + bool (*down) (iface_t *this); + bool (*add_addr) (iface_t *this, host_t *addr); + iterator_t* (*create_addr_iterator) (iface_t *this); + */ + + /** + * @brief Destroy an interface + */ + void (*destroy) (iface_t *this); +}; + +/** + * @brief Create a new interface for a guest + * + * @param guest name of the guest for this interface + * @param guestif name of the interface in the guest + * @param mconsole mconsole of guest + * @return interface descriptor, or NULL if failed + */ +iface_t *iface_create(char *guest, char *guestif, mconsole_t *mconsole); + +#endif /* IFACE_H */ + diff --git a/src/dumm/main.c b/src/dumm/main.c new file mode 100644 index 000000000..d6e142e24 --- /dev/null +++ b/src/dumm/main.c @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> +#include <library.h> +#include <readline/readline.h> +#include <readline/history.h> +#include <dlfcn.h> +#include <dirent.h> + +#include "dumm.h" + +/** + * global set of UMLs guests + */ +dumm_t *dumm; + +/** + * show usage information (program arguments) + */ +static void usage() +{ + printf("Usage:\n"); + printf(" --dir|-d <path> set working dir to <path>\n"); + printf(" --help|-h show this help\n"); +} + +/** + * readline() wrapper + */ +static char* get_line(char *format, ...) +{ + char *line = NULL; + char *prompt = ""; + va_list args; + + va_start(args, format); + vasprintf(&prompt, format, args); + va_end(args); + + while (TRUE) + { + line = readline(prompt); + if (line == NULL) + { + printf("quit\n"); + dumm->destroy(dumm); + clear_history(); + exit(0); + } + if (*line == '\0') + { + free(line); + continue; + } + add_history(line); + break; + } + free(prompt); + return line; +} + +/** + * get a guest by name + */ +static guest_t* get_guest(char *name) +{ + iterator_t *iterator; + guest_t *guest = NULL; + + iterator = dumm->create_guest_iterator(dumm); + while (iterator->iterate(iterator, (void**)&guest)) + { + if (streq(guest->get_name(guest), name)) + { + break; + } + guest = NULL; + } + iterator->destroy(iterator); + return guest; +} + +/** + * get a bridge by name + */ +static bridge_t* get_bridge(char *name) +{ + iterator_t *iterator; + bridge_t *bridge = NULL; + + iterator = dumm->create_bridge_iterator(dumm); + while (iterator->iterate(iterator, (void**)&bridge)) + { + if (streq(bridge->get_name(bridge), name)) + { + break; + } + bridge = NULL; + } + iterator->destroy(iterator); + return bridge; +} + +/** + * get an interface by guest name + */ +static iface_t* get_iface(char *name, char *ifname) +{ + iterator_t *guests, *ifaces; + guest_t *guest; + iface_t *iface; + + guests = dumm->create_guest_iterator(dumm); + while (guests->iterate(guests, (void**)&guest)) + { + if (streq(guest->get_name(guest), name)) + { + iface = NULL; + ifaces = guest->create_iface_iterator(guest); + while (ifaces->iterate(ifaces, (void**)&iface)) + { + if (streq(iface->get_guestif(iface), ifname)) + { + break; + } + iface = NULL; + } + ifaces->destroy(ifaces); + if (iface) + { + break; + } + } + } + guests->destroy(guests); + return iface; +} + +static void guest_addif_menu(guest_t *guest) +{ + char *name; + + name = get_line("interface name: "); + + if (!guest->create_iface(guest, name)) + { + printf("creating interface failed\n"); + } + free(name); +} + +static void guest_delif_menu(guest_t *guest) +{ + char *name; + iface_t *iface; + iterator_t *iterator; + bool found = FALSE; + + name = get_line("interface name: "); + + iterator = guest->create_iface_iterator(guest); + while (iterator->iterate(iterator, (void**)&iface)) + { + if (streq(iface->get_guestif(iface), name)) + { + iterator->remove(iterator); + iface->destroy(iface); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + + if (!found) + { + printf("interface '%s' not found\n"); + } + free(name); +} + +static void guest_console(guest_t *guest) +{ + int con; + + for (con = 1; con <= 6; con++) + { + char *pts = guest->get_console(guest, con); + if (pts) + { + printf("%d: %s\n", con, pts); + free(pts); + } + } +} + +static void guest_menu(guest_t *guest) +{ + while (TRUE) + { + char *line = get_line("guest/%s# ", guest->get_name(guest)); + + if (streq(line, "back")) + { + free(line); + break; + } + else if (streq(line, "start")) + { + if (guest->start(guest)) + { + printf("guest '%s' is running\n", guest->get_name(guest)); + } + else + { + printf("failed to start guest '%s'\n", guest->get_name(guest)); + } + } + else if (streq(line, "stop")) + { + printf("stopping guest '%s'...\n", guest->get_name(guest)); + guest->stop(guest); + printf("guest '%s' is down\n", guest->get_name(guest)); + } + else if (streq(line, "addif")) + { + guest_addif_menu(guest); + } + else if (streq(line, "delif")) + { + guest_delif_menu(guest); + } + else if (streq(line, "console")) + { + guest_console(guest); + } + else + { + printf("back|start|stop|addif|delif|console\n"); + } + free(line); + } +} + +static void guest_create_menu() +{ + char *name, *kernel, *master, *mem; + guest_t *guest; + + name = get_line("guest name: "); + kernel = get_line("kernel image: "); + master = get_line("master filesystem: "); + mem = get_line("amount of memory in MB: "); + + guest = dumm->create_guest(dumm, name, kernel, master, atoi(mem)); + if (guest) + { + printf("guest '%s' created\n", guest->get_name(guest)); + guest_menu(guest); + } + else + { + printf("failed to create guest '%s'\n", name); + } + free(name); + free(kernel); + free(master); + free(mem); +} + +static void guest_list_menu() +{ + while (TRUE) + { + iterator_t *iterator; + guest_t *guest; + char *line = get_line("guest# "); + + if (streq(line, "back")) + { + free(line); + break; + } + else if (streq(line, "list")) + { + iterator = dumm->create_guest_iterator(dumm); + while (iterator->iterate(iterator, (void**)&guest)) + { + printf("%s\n", guest->get_name(guest)); + } + iterator->destroy(iterator); + } + else if (streq(line, "create")) + { + guest_create_menu(); + } + else + { + guest = get_guest(line); + if (guest) + { + guest_menu(guest); + } + else + { + printf("back|list|create|<guest>\n"); + } + } + free(line); + } +} + +static void bridge_addif_menu(bridge_t *bridge) +{ + char *name, *ifname; + iface_t *iface; + + name = get_line("guest name: "); + ifname = get_line("interface name: "); + + iface = get_iface(name, ifname); + if (!iface) + { + printf("guest '%s' has no interface named '%s'\n", name, ifname); + } + else if (!bridge->connect_iface(bridge, iface)) + { + printf("failed to add interface '%s' to bridge '%s'\n", ifname, + bridge->get_name(bridge)); + } + free(name); + free(ifname); +} + +static void bridge_delif_menu(bridge_t *bridge) +{ + char *name, *ifname; + iface_t *iface; + + name = get_line("guest name: "); + ifname = get_line("interface name: "); + + iface = get_iface(name, ifname); + if (!iface) + { + printf("guest '%s' has no interface named '%s'\n", name, ifname); + } + else if (!bridge->disconnect_iface(bridge, iface)) + { + printf("failed to remove interface '%s' from bridge '%s'\n", ifname, + bridge->get_name(bridge)); + } + free(name); + free(ifname); +} + +static void bridge_menu(bridge_t *bridge) +{ + while (TRUE) + { + char *line = get_line("bridge/%s# ", bridge->get_name(bridge)); + + if (streq(line, "back")) + { + free(line); + break; + } + else if (streq(line, "list")) + { + iterator_t *iterator; + iface_t *iface; + + iterator = bridge->create_iface_iterator(bridge); + while (iterator->iterate(iterator, (void**)&iface)) + { + printf("%s (%s)\n", iface->get_guestif(iface), iface->get_hostif(iface)); + } + iterator->destroy(iterator); + } + else if (streq(line, "addif")) + { + bridge_addif_menu(bridge); + } + else if (streq(line, "delif")) + { + bridge_delif_menu(bridge); + } + else + { + printf("back|list|addif|delif\n"); + } + free(line); + } +} + +static void bridge_create_menu() +{ + char *name; + bridge_t *bridge; + + name = get_line("bridge name: "); + + bridge = dumm->create_bridge(dumm, name); + if (bridge) + { + printf("bridge '%s' created\n", bridge->get_name(bridge)); + bridge_menu(bridge); + } + else + { + printf("failed to create bridge '%s'\n", name); + } + free(name); +} + +static void bridge_list_menu() +{ + while (TRUE) + { + iterator_t *iterator; + bridge_t *bridge; + char *line = get_line("bridge# "); + + if (streq(line, "back")) + { + free(line); + break; + } + else if (streq(line, "list")) + { + iterator = dumm->create_bridge_iterator(dumm); + while (iterator->iterate(iterator, (void**)&bridge)) + { + printf("%s\n", bridge->get_name(bridge)); + } + iterator->destroy(iterator); + } + else if (streq(line, "create")) + { + bridge_create_menu(); + } + else + { + bridge = get_bridge(line); + if (bridge) + { + bridge_menu(bridge); + } + else + { + printf("back|list|create|<bridge>\n"); + } + } + free(line); + } +} + +static void template_menu() +{ + char *name; + + name = get_line("template name (or 'none'): "); + + dumm->load_template(dumm, streq(name, "none") ? NULL : name); + + free(name); +} + +typedef bool (*uml_test_t)(dumm_t *dumm); + +static void test_menu() +{ + char *name; + void *handle; + struct dirent *ent; + DIR *dir; + uml_test_t test; + + name = get_line("test name: "); + + dir = opendir("tests"); + if (dir) + { + while ((ent = readdir(dir))) + { + char buf[PATH_MAX]; + size_t len; + + len = strlen(ent->d_name); + if (strlen(ent->d_name) < 4 || !streq(ent->d_name + len - 3, ".so")) + { + continue; + } + + snprintf(buf, sizeof(buf), "%s/%s", "tests", ent->d_name); + handle = dlopen(buf, RTLD_LAZY); + if (!handle) + { + printf("failed to open test %s\n", ent->d_name); + continue; + } + test = dlsym(handle, "test"); + if (test && dumm->load_template(dumm, ent->d_name)) + { + printf("running test %s: ", ent->d_name); + if (test(dumm)) + { + printf("success\n"); + } + else + { + printf("failed\n"); + } + } + else + { + printf("failed to open test %s\n", ent->d_name); + } + dlclose(handle); + } + } + free(name); +} + +/** + * Signal handler + */ +void signal_action(int sig, siginfo_t *info, void *ucontext) +{ + dumm->destroy(dumm); + clear_history(); + exit(0); +} + +/** + * main routine, parses args and reads from console + */ +int main(int argc, char *argv[]) +{ + struct sigaction action; + char *dir = "."; + + while (TRUE) + { + struct option options[] = { + {"dir", 1, 0, 0}, + {"help", 0, 0, 0}, + {0, 0, 0, 0} + }; + + switch (getopt_long(argc, argv, "d:h", options, NULL)) + { + case -1: + break; + case 'd': + dir = optarg; + continue; + case 'h': + usage(); + return 0; + default: + usage(); + return 1; + } + break; + } + + memset(&action, 0, sizeof(action)); + action.sa_sigaction = signal_action; + action.sa_flags = SA_SIGINFO; + if (sigaction(SIGINT, &action, NULL) != 0 || + sigaction(SIGQUIT, &action, NULL) != 0 || + sigaction(SIGTERM, &action, NULL) != 0) + { + printf("signal handler setup failed: %m.\n"); + return 1; + } + + dumm = dumm_create(dir); + while (TRUE) + { + char *line = get_line("# "); + + if (streq(line, "quit")) + { + free(line); + break; + } + else if (streq(line, "guest")) + { + guest_list_menu(); + } + else if (streq(line, "bridge")) + { + bridge_list_menu(); + } + else if (streq(line, "template")) + { + template_menu(); + } + else if (streq(line, "test")) + { + test_menu(); + } + else + { + printf("quit|guest|bridge|template|test\n"); + } + free(line); + } + dumm->load_template(dumm, NULL); + dumm->destroy(dumm); + clear_history(); + return 0; +} + diff --git a/src/dumm/mconsole.c b/src/dumm/mconsole.c new file mode 100644 index 000000000..25cb84621 --- /dev/null +++ b/src/dumm/mconsole.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2001-2004 Jeff Dike + * + * Based on the "uml_mconsole" utility from Jeff Dike. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/socket.h> +#include <errno.h> +#include <sys/un.h> + +#include <debug.h> + +#include "mconsole.h" + +#define MCONSOLE_MAGIC 0xcafebabe +#define MCONSOLE_VERSION 2 +#define MCONSOLE_MAX_DATA 512 + +typedef struct private_mconsole_t private_mconsole_t; + +struct private_mconsole_t { + /** public interface */ + mconsole_t public; + /** mconsole socket */ + int console; + /** notify socket */ + int notify; + /** address of uml socket */ + struct sockaddr_un uml; +}; + +/** + * mconsole message format from "arch/um/include/mconsole.h" + */ +typedef struct mconsole_request mconsole_request; +/** mconsole request message */ +struct mconsole_request { + u_int32_t magic; + u_int32_t version; + u_int32_t len; + char data[MCONSOLE_MAX_DATA]; +}; + + +typedef struct mconsole_reply mconsole_reply; +/** mconsole reply message */ +struct mconsole_reply { + u_int32_t err; + u_int32_t more; + u_int32_t len; + char data[MCONSOLE_MAX_DATA]; +}; + +typedef struct mconsole_notify mconsole_notify; +/** mconsole notify message */ +struct mconsole_notify { + u_int32_t magic; + u_int32_t version; + enum { + MCONSOLE_SOCKET, + MCONSOLE_PANIC, + MCONSOLE_HANG, + MCONSOLE_USER_NOTIFY, + } type; + u_int32_t len; + char data[MCONSOLE_MAX_DATA]; +}; + +/** + * send a request to UML using mconsole + */ +static int request(private_mconsole_t *this, char *command, + char buf[], size_t *size) +{ + mconsole_request request; + mconsole_reply reply; + int len, total = 0; + + memset(&request, 0, sizeof(request)); + request.magic = MCONSOLE_MAGIC; + request.version = MCONSOLE_VERSION; + request.len = min(strlen(command), sizeof(reply.data) - 1); + strncpy(request.data, command, request.len); + *buf = '\0'; + (*size)--; + + if (sendto(this->console, &request, sizeof(request), 0, + (struct sockaddr*)&this->uml, sizeof(this->uml)) < 0) + { + snprintf(buf, *size, "sending mconsole command to UML failed: %m"); + return -1; + } + do + { + len = recv(this->console, &reply, sizeof(reply), 0); + if (len < 0) + { + snprintf(buf, *size, "receiving from mconsole failed: %m"); + return -1; + } + if (len > 0) + { + strncat(buf, reply.data, min(reply.len, *size - total)); + total += reply.len; + } + } + while (reply.more); + + *size = total; + return reply.err; +} + +/** + * Implementation of mconsole_t.add_iface. + */ +static bool add_iface(private_mconsole_t *this, char *guest, char *host) +{ + char buf[128]; + int len; + + len = snprintf(buf, sizeof(buf), "config %s=tuntap,%s", guest, host); + if (len < 0 || len >= sizeof(buf)) + { + return FALSE; + } + len = sizeof(buf); + if (request(this, buf, buf, &len) != 0) + { + DBG1("adding interface failed: %.*s", len, buf); + return FALSE; + } + return TRUE; +} + +/** + * Implementation of mconsole_t.del_iface. + */ +static bool del_iface(private_mconsole_t *this, char *guest) +{ + char buf[128]; + int len; + + len = snprintf(buf, sizeof(buf), "remove %s", guest); + if (len < 0 || len >= sizeof(buf)) + { + return FALSE; + } + if (request(this, buf, buf, &len) != 0) + { + DBG1("removing interface failed: %.*s", len, buf); + return FALSE; + } + return TRUE; +} + +/** + * Implementation of mconsole_t.get_console_pts. + */ +static char* get_console_pts(private_mconsole_t *this, int con) +{ + char buf[128]; + char *pos; + int len; + + len = snprintf(buf, sizeof(buf), "config con%d", con); + if (len < 0 || len >= sizeof(buf)) + { + return NULL; + } + len = sizeof(buf); + if (request(this, buf, buf, &len) != 0) + { + DBG1("getting console pts failed: %.*s", len, buf); + return NULL; + } + pos = memchr(buf, ':', len); + if (pos == NULL) + { + return NULL; + } + pos++; + return strndup(pos, len - (pos - buf)); +} + +/** + * Poll until guest is ready + */ +static bool wait_bootup(private_mconsole_t *this) +{ + char *cmd, buf[128]; + int len, res; + + cmd = "config con0"; + while (TRUE) + { + len = sizeof(buf); + res = request(this, cmd, buf, &len); + if (res < 0) + { + return FALSE; + } + if (res == 0) + { + return TRUE; + } + usleep(50000); + } +} + +/** + * Implementation of mconsole_t.destroy. + */ +static void destroy(private_mconsole_t *this) +{ + close(this->console); + close(this->notify); + free(this); +} + +/** + * setup the mconsole notify connection and wait for its readyness + */ +static bool wait_for_notify(private_mconsole_t *this, char *nsock) +{ + struct sockaddr_un addr; + mconsole_notify notify; + int len; + + this->notify = socket(AF_UNIX, SOCK_DGRAM, 0); + if (this->notify < 0) + { + DBG1("opening mconsole notify socket failed: %m"); + return FALSE; + } + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, nsock, sizeof(addr)); + if (bind(this->notify, (struct sockaddr*)&addr, sizeof(addr)) < 0) + { + DBG1("binding mconsole notify socket to '%s' failed: %m", nsock); + close(this->notify); + return FALSE; + } + do + { + len = recvfrom(this->notify, ¬ify, sizeof(notify), 0, NULL, 0); + } while (len < 0 && errno == EINTR); + if (len < 0 || len >= sizeof(notify)) + { + DBG1("reading from mconsole notify socket failed: %m"); + close(this->notify); + unlink(nsock); + return FALSE; + } + if (notify.magic != MCONSOLE_MAGIC || + notify.version != MCONSOLE_VERSION || + notify.type != MCONSOLE_SOCKET) + { + DBG1("received unexpected message from mconsole notify socket: %b", + ¬ify, sizeof(notify)); + close(this->notify); + unlink(nsock); + return FALSE; + } + memset(&this->uml, 0, sizeof(this->uml)); + this->uml.sun_family = AF_UNIX; + strncpy(this->uml.sun_path, (char*)¬ify.data, sizeof(this->uml.sun_path)); + return TRUE; +} + +/** + * setup the mconsole console connection + */ +static bool setup_console(private_mconsole_t *this) +{ + struct sockaddr_un addr; + + this->console = socket(AF_UNIX, SOCK_DGRAM, 0); + if (this->console < 0) + { + DBG1("opening mconsole socket failed: %m"); + return FALSE; + } + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(&addr.sun_path[1], sizeof(addr.sun_path), "%5d-%d", + getpid(), this->console); + if (bind(this->console, (struct sockaddr*)&addr, sizeof(addr)) < 0) + { + DBG1("binding mconsole socket to '%s' failed: %m", &addr.sun_path[1]); + close(this->console); + return FALSE; + } + return TRUE; +} + +/** + * create the mconsole instance + */ +mconsole_t *mconsole_create(char *notify) +{ + private_mconsole_t *this = malloc_thing(private_mconsole_t); + + this->public.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface; + this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_iface; + this->public.get_console_pts = (char*(*)(mconsole_t*, int con))get_console_pts; + this->public.destroy = (void*)destroy; + + if (!wait_for_notify(this, notify)) + { + free(this); + return NULL; + } + + if (!setup_console(this)) + { + close(this->notify); + unlink(notify); + free(this); + return NULL; + } + unlink(notify); + + if (!wait_bootup(this)) + { + destroy(this); + return NULL; + } + + return &this->public; +} + diff --git a/src/dumm/mconsole.h b/src/dumm/mconsole.h new file mode 100644 index 000000000..53aaa1b8b --- /dev/null +++ b/src/dumm/mconsole.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef MCONSOLE_H +#define MCONSOLE_H + +#include <library.h> + +typedef struct mconsole_t mconsole_t; + +/** + * @brief UML mconsole, change running UML configuration using mconsole. + */ +struct mconsole_t { + + /** + * @brief Create a guest interface and connect it to tap host interface. + * + * @param guest name of the interface to create in the guest + * @param host name of the tap device to connect guest to + * @return TRUE if interface created + */ + bool (*add_iface)(mconsole_t *this, char *guest, char *host); + + /** + * @brief Delete a guest interface. + * + * @param guest name of the interface to delete on the guest + * @return TRUE if interface deleted + */ + bool (*del_iface)(mconsole_t *this, char *guest); + + /** + * @brief Get the pts device file assigned to a console. + * + * @param con console number in guest + * @return allocated device string + */ + char* (*get_console_pts)(mconsole_t *this, int con); + + /** + * @brief Destroy the mconsole instance + */ + void (*destroy) (mconsole_t *this); +}; + +/** + * @brief Create a new mconsole connection to a guest. + * + * Waits for a notification from the guest through the notify socket and tries + * to connect to the mconsole socket supplied in the received notification. + * + * @param notify unix notify socket path + * @return mconsole instance, or NULL if failed + */ +mconsole_t *mconsole_create(char *notify); + +#endif /* MCONSOLE_H */ + |