From 0eec0b60f0c5b3d94d5e382ea3d4aff35c879ed2 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Thu, 12 Apr 2012 05:24:50 -0400 Subject: [PATCH] Linux: Use our own signalfd wrapper, rather than libstddjb selfpipe already does a fine job of interfacing to signalfd. But Debian and Ubuntu want us to depend on the skalibs-dev package rather than build libstddjb ourselves. That would be fine except that skalibs-dev has static libraries only, and they aren't built with -fPIC. This interferes with building mosh-{client,server} as position-independent executables, which is a desirable security measure. So we have our own wrapper, which invokes either signalfd or selfpipe. And we build it ourselves with our own flags, because it's part of the Mosh project proper. (closes #108) --- configure.ac | 26 ++++++++++--- debian/control | 2 +- debian/rules | 3 +- src/examples/benchmark.cc | 5 +-- src/examples/termemu.cc | 13 +++---- src/frontend/mosh-server.cc | 17 ++++----- src/frontend/stmclient.cc | 25 ++++++------- src/util/Makefile.am | 6 ++- src/util/sigfd.cc | 75 +++++++++++++++++++++++++++++++++++++ src/util/sigfd.h | 54 ++++++++++++++++++++++++++ 10 files changed, 180 insertions(+), 46 deletions(-) create mode 100644 src/util/sigfd.cc create mode 100644 src/util/sigfd.h diff --git a/configure.ac b/configure.ac index 68f4522..804b6fc 100644 --- a/configure.ac +++ b/configure.ac @@ -176,13 +176,32 @@ AC_ARG_WITH([skalibs-libdir], [specify exact library dir for skalibs libraries])], [SKALIBS_LDFLAGS="-L$withval"]) -AM_CONDITIONAL([COND_THIRD_LIBSTDDJB], [test x"$with_skalibs" = xno]) +STDDJB_CPPFLAGS="" +STDDJB_LDFLAGS="" AS_IF([test x"$with_skalibs" != xno], [AX_CHECK_LIBRARY([SKALIBS], [selfpipe.h], [stddjb], [], [AC_MSG_ERROR([Unable to find skalibs.])]) AC_SUBST([STDDJB_CPPFLAGS], ["$SKALIBS_CPPFLAGS"]) AC_SUBST([STDDJB_LDFLAGS], ["$SKALIBS_LDFLAGS -lstddjb"])]) +have_signalfd="no" +AC_CHECK_DECL([signalfd], + [have_signalfd="yes" + AC_DEFINE([HAVE_SIGNALFD], [1], + [Define if signalfd is available.])], + , [[#include ]]) + +AS_IF([test x"$have_signalfd" = xno], + [AC_DEFINE([USE_LIBSTDDJB], [1], + [Define if we should call functions from libstddjb (part of skalibs)])]) +AM_CONDITIONAL([USE_LIBSTDDJB], + [test x"$have_signalfd" = xno]) + +# Build the bundled libstddjb only if we'll use it and we don't have a +# path for skalibs. +AM_CONDITIONAL([COND_THIRD_LIBSTDDJB], + [test x"$have_signalfd" = xno && test x"$with_skalibs" = xno]) + # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h langinfo.h limits.h locale.h netinet/in.h stddef.h stdint.h inttypes.h stdlib.h string.h sys/ioctl.h sys/resource.h sys/socket.h sys/time.h term.h termios.h unistd.h wchar.h wctype.h], [], [AC_MSG_ERROR([Missing required header file.])]) @@ -239,11 +258,6 @@ AC_CHECK_DECL([pipe2], , [[#define _GNU_SOURCE #include ]]) -AC_CHECK_DECL([signalfd], - [AC_DEFINE([HAVE_SIGNALFD], [1], - [Define if signalfd is available.])], - , [[#include ]]) - AC_CHECK_DECL([forkpty], [AC_DEFINE([FORKPTY_IN_LIBUTIL], [1], [Define if libutil.h necessary for forkpty().])], diff --git a/debian/control b/debian/control index b28911e..b7400b4 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: mosh Section: net Priority: optional Maintainer: Keith Winstein -Build-Depends: debhelper (>= 7.0.50), autotools-dev, protobuf-compiler, libprotobuf-dev, dh-autoreconf, pkg-config, libutempter-dev, libboost-dev, zlib1g-dev, skalibs-dev, libncurses5-dev +Build-Depends: debhelper (>= 7.0.50), autotools-dev, protobuf-compiler, libprotobuf-dev, dh-autoreconf, pkg-config, libutempter-dev, libboost-dev, zlib1g-dev, libncurses5-dev Standards-Version: 3.9.3 Homepage: http://mosh.mit.edu Vcs-Git: git://github.com/keithw/mosh.git diff --git a/debian/rules b/debian/rules index c4e5743..9a90c54 100755 --- a/debian/rules +++ b/debian/rules @@ -15,5 +15,4 @@ override_dh_auto_configure: dh_auto_configure -- \ --disable-silent-rules \ - --enable-compile-warnings=error \ - --with-skalibs=/usr/lib/skalibs + --enable-compile-warnings=error diff --git a/src/examples/benchmark.cc b/src/examples/benchmark.cc index 923c3c9..cecea4e 100644 --- a/src/examples/benchmark.cc +++ b/src/examples/benchmark.cc @@ -38,10 +38,7 @@ #include #endif -extern "C" { -#include "selfpipe.h" -} - +#include "sigfd.h" #include "swrite.h" #include "completeterminal.h" #include "user.h" diff --git a/src/examples/termemu.cc b/src/examples/termemu.cc index f43afb6..9f7fd51 100644 --- a/src/examples/termemu.cc +++ b/src/examples/termemu.cc @@ -50,10 +50,7 @@ #include "swrite.h" #include "fatal_assert.h" #include "locale_utils.h" - -extern "C" { -#include "selfpipe.h" -} +#include "sigfd.h" const size_t buf_size = 16384; @@ -197,13 +194,13 @@ bool tick( Terminal::Framebuffer &state, Terminal::Framebuffer &new_frame, void emulate_terminal( int fd ) { /* establish WINCH fd and start listening for signal */ - int signal_fd = selfpipe_init(); + int signal_fd = sigfd_init(); if ( signal_fd < 0 ) { - perror( "selfpipe" ); + perror( "sigfd_init" ); return; } - fatal_assert( selfpipe_trap(SIGWINCH) == 0 ); + fatal_assert( sigfd_trap(SIGWINCH) == 0 ); /* get current window size */ struct winsize window_size; @@ -291,7 +288,7 @@ void emulate_terminal( int fd ) } } else if ( pollfds[ 2 ].revents & POLLIN ) { /* resize */ - fatal_assert( selfpipe_read() == SIGWINCH ); + fatal_assert( sigfd_read() == SIGWINCH ); /* get new size */ if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { diff --git a/src/frontend/mosh-server.cc b/src/frontend/mosh-server.cc index 9a00d42..0b2773d 100644 --- a/src/frontend/mosh-server.cc +++ b/src/frontend/mosh-server.cc @@ -40,10 +40,7 @@ #include #include -extern "C" { -#include "selfpipe.h" -} - +#include "sigfd.h" #include "completeterminal.h" #include "swrite.h" #include "user.h" @@ -367,14 +364,14 @@ int run_server( const char *desired_ip, const char *desired_port, void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network ) { /* establish fd for shutdown signals */ - int signal_fd = selfpipe_init(); + int signal_fd = sigfd_init(); if ( signal_fd < 0 ) { - perror( "selfpipe_init" ); + perror( "sigfd_init" ); return; } - fatal_assert( selfpipe_trap( SIGTERM ) == 0 ); - fatal_assert( selfpipe_trap( SIGINT ) == 0 ); + fatal_assert( sigfd_trap( SIGTERM ) == 0 ); + fatal_assert( sigfd_trap( SIGINT ) == 0 ); /* prepare to poll for events */ struct pollfd pollfds[ 3 ]; @@ -516,11 +513,11 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network if ( pollfds[ 2 ].revents & POLLIN ) { /* shutdown signal */ - int signo = selfpipe_read(); + int signo = sigfd_read(); if ( signo == 0 ) { break; } else if ( signo < 0 ) { - perror( "selfpipe_read" ); + perror( "sigfd_read" ); break; } diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc index 5a57783..f9cfb67 100644 --- a/src/frontend/stmclient.cc +++ b/src/frontend/stmclient.cc @@ -37,10 +37,7 @@ #include #endif -extern "C" { -#include "selfpipe.h" -} - +#include "sigfd.h" #include "stmclient.h" #include "swrite.h" #include "completeterminal.h" @@ -105,19 +102,19 @@ void STMClient::shutdown( void ) void STMClient::main_init( void ) { /* establish a fd for signals */ - signal_fd = selfpipe_init(); + signal_fd = sigfd_init(); if ( signal_fd < 0 ) { - perror( "selfpipe_init" ); + perror( "sigfd_init" ); return; } - fatal_assert( selfpipe_trap( SIGWINCH ) == 0 ); - fatal_assert( selfpipe_trap( SIGTERM ) == 0 ); - fatal_assert( selfpipe_trap( SIGINT ) == 0 ); - fatal_assert( selfpipe_trap( SIGHUP ) == 0 ); - fatal_assert( selfpipe_trap( SIGPIPE ) == 0 ); - fatal_assert( selfpipe_trap( SIGTSTP ) == 0 ); - fatal_assert( selfpipe_trap( SIGCONT ) == 0 ); + fatal_assert( sigfd_trap( SIGWINCH ) == 0 ); + fatal_assert( sigfd_trap( SIGTERM ) == 0 ); + fatal_assert( sigfd_trap( SIGINT ) == 0 ); + fatal_assert( sigfd_trap( SIGHUP ) == 0 ); + fatal_assert( sigfd_trap( SIGPIPE ) == 0 ); + fatal_assert( sigfd_trap( SIGTSTP ) == 0 ); + fatal_assert( sigfd_trap( SIGCONT ) == 0 ); /* get initial window size */ if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { @@ -325,7 +322,7 @@ void STMClient::main( void ) } if ( pollfds[ 2 ].revents & POLLIN ) { - int signo = selfpipe_read(); + int signo = sigfd_read(); if ( signo == SIGWINCH ) { /* resize */ diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 1ab3133..ad4ed8d 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -2,4 +2,8 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXF noinst_LIBRARIES = libmoshutil.a -libmoshutil_a_SOURCES = locale_utils.cc locale_utils.h swrite.cc swrite.h dos_assert.h fatal_assert.h +libmoshutil_a_SOURCES = locale_utils.cc locale_utils.h swrite.cc swrite.h dos_assert.h fatal_assert.h sigfd.h + +if !USE_LIBSTDDJB + libmoshutil_a_SOURCES += sigfd.cc +endif diff --git a/src/util/sigfd.cc b/src/util/sigfd.cc new file mode 100644 index 0000000..824f453 --- /dev/null +++ b/src/util/sigfd.cc @@ -0,0 +1,75 @@ +/* + Mosh: the mobile shell + Copyright 2012 Keith Winstein + + 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 3 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include + +#include "fatal_assert.h" + +static sigset_t caught; +static int fd = -1; + +#define SIGNALFD_FLAGS ( SFD_NONBLOCK | SFD_CLOEXEC ) + +int sigfd_init( void ) +{ + if ( fd != -1 ) { + errno = EBUSY; + return -1; + } + sigemptyset( &caught ); + fd = signalfd( -1, &caught, SIGNALFD_FLAGS ); + return fd; +} + +int sigfd_trap( int sig ) +{ + /* The callers all fatal_assert on error, so we can do the same here. + We still return 'int' in order to have the same API as libstddjb. */ + fatal_assert( 0 <= fd ); + fatal_assert( 0 <= sigaddset( &caught, sig ) ); + fatal_assert( 0 <= sigprocmask( SIG_BLOCK, &caught, NULL ) ); + fatal_assert( 0 <= signalfd( fd, &caught, SIGNALFD_FLAGS ) ); + return 0; +} + +int sigfd_read( void ) +{ + int r; + struct signalfd_siginfo si; + + do { + r = read( fd, &si, sizeof( si ) ); + } while ( ( r == -1 ) && ( errno == EINTR ) ); + + if ( r == -1 ) { + if ( errno == EAGAIN || errno == EWOULDBLOCK ) + /* No signal available */ + return 0; + return -1; + } else if ( r != sizeof( si ) ) { + /* Should never happen? + Includes r = 0, i.e. end of file */ + errno = EPIPE; + return -1; + } + + return (int) si.ssi_signo; +} diff --git a/src/util/sigfd.h b/src/util/sigfd.h new file mode 100644 index 0000000..d8d7905 --- /dev/null +++ b/src/util/sigfd.h @@ -0,0 +1,54 @@ +/* + Mosh: the mobile shell + Copyright 2012 Keith Winstein + + 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 3 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Uses either signalfd or libstddjb's selfpipe to receive signals as part of + an event loop. + + selfpipe already does a fine job of interfacing to signalfd. But Debian and + Ubuntu want us to depend on the skalibs-dev package rather than build + libstddjb ourselves. That would be fine except that skalibs-dev has static + libraries only, and they aren't built with -fPIC. This interferes with + building mosh-{client,server} as position-independent executables, which + is a desirable security measure. + + So we have our own wrapper, which invokes either signalfd or selfpipe. And + we build it ourselves with our own flags, because it's part of the Mosh + project proper. */ + +#ifndef SIGFD_HPP +#define SIGFD_HPP + +#if USE_LIBSTDDJB + +extern "C" { +#include "selfpipe.h" +} + +#define sigfd_init selfpipe_init +#define sigfd_trap selfpipe_trap +#define sigfd_read selfpipe_read + +#else + +int sigfd_init( void ); +int sigfd_trap( int sig ); +int sigfd_read( void ); + +#endif + +#endif