summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJoey Hess <joeyh@joeyh.name>2017-05-05 14:27:39 -0400
committerJoey Hess <joeyh@joeyh.name>2017-05-05 14:30:46 -0400
commit2f5315b3c32fbd23ebce05da80712183dc259b7c (patch)
tree48de39738910859cb0eaaf15cbfe9529b44e4272
parent4a2a01a7eaa8945d063468d7d1c24f095a3ae2a4 (diff)
downloaddebug-me-2f5315b3c32fbd23ebce05da80712183dc259b7c.tar.gz
standalone tarball
Adapted the standalone tarball building code from git-annex.
-rw-r--r--.gitignore4
-rw-r--r--Build/LinuxMkLibs.hs102
-rw-r--r--ControlWindow.hs9
-rw-r--r--Makefile54
-rw-r--r--Utility/LinuxMkLibs.hs60
-rw-r--r--standalone/linux/skel/README6
-rwxr-xr-xstandalone/linux/skel/runshell80
7 files changed, 298 insertions, 17 deletions
diff --git a/.gitignore b/.gitignore
index 3a34823..01586cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,7 @@ dist/*
.stack-work/*
debug-me
doc/.ikiwiki
+*.hi
+*.o
+Build/LinuxMkLibs
+tmp
diff --git a/Build/LinuxMkLibs.hs b/Build/LinuxMkLibs.hs
new file mode 100644
index 0000000..3b4f7f5
--- /dev/null
+++ b/Build/LinuxMkLibs.hs
@@ -0,0 +1,102 @@
+{- Linux library copier and binary shimmer
+ -
+ - Copyright 2013 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Main where
+
+import System.Process
+import System.Directory hiding (isSymbolicLink)
+import System.Environment
+import Data.Maybe
+import System.FilePath
+import Control.Monad
+import Data.List
+import System.Posix.Files
+import Control.Applicative
+import Prelude
+
+import Utility.LinuxMkLibs
+
+main :: IO ()
+main = getArgs >>= go
+ where
+ go [] = error "specify LINUXSTANDALONE_DIST"
+ go (top:_) = mklibs top
+
+mklibs :: FilePath -> IO ()
+mklibs top = do
+ fs <- lines <$> readProcess "find" [top, "-type", "f"] ""
+ exes <- filterM checkExe fs
+ libs <- parseLdd <$> readProcess "ldd" exes ""
+ glibclibs <- glibcLibs
+ let libs' = nub $ libs ++ glibclibs
+ libdirs <- nub . catMaybes <$> mapM (installLib installFile top) libs'
+
+ -- Various files used by runshell to set up env vars used by the
+ -- linker shims.
+ writeFile (top </> "libdirs") (unlines libdirs)
+ writeFile (top </> "gconvdir")
+ (takeDirectory $ Prelude.head $ filter ("/gconv/" `isInfixOf`) glibclibs)
+
+ let linker = Prelude.head $ filter ("ld-linux" `isInfixOf`) libs'
+ mapM_ (installLinkerShim top linker) exes
+
+{- Installs a linker shim script around a binary.
+ -
+ - Note that each binary is put into its own separate directory,
+ - to avoid eg git looking for binaries in its directory rather
+ - than in PATH.
+ -}
+installLinkerShim :: FilePath -> FilePath -> FilePath -> IO ()
+installLinkerShim top linker exe = do
+ createDirectoryIfMissing True (top </> shimdir)
+ createDirectoryIfMissing True (top </> exedir)
+ islink <- isSymbolicLink <$> getSymbolicLinkStatus exe
+ if islink
+ then do
+ sl <- readSymbolicLink exe
+ removeFile exe
+ removeFile exedest
+ -- Assume that for a symlink, the destination
+ -- will also be shimmed.
+ let sl' = ".." </> takeFileName sl </> takeFileName sl
+ createSymbolicLink sl' exedest
+ else renameFile exe exedest
+ writeFile exe $ unlines
+ [ "#!/bin/sh"
+ , "exec \"$DEBUG_ME_DIR/" ++ linker ++ "\" --library-path \"$DEBUG_ME_LD_LIBRARY_PATH\" \"$DEBUG_ME_DIR/shimmed/" ++ base ++ "/" ++ base ++ "\" \"$@\""
+ ]
+ setFileMode exe $ ownerExecuteMode
+ `unionFileModes` groupExecuteMode
+ `unionFileModes` otherExecuteMode
+ `unionFileModes` ownerReadMode
+ `unionFileModes` groupReadMode
+ `unionFileModes` otherReadMode
+ where
+ base = takeFileName exe
+ shimdir = "shimmed" </> base
+ exedir = "exe"
+ exedest = top </> shimdir </> base
+
+installFile :: FilePath -> FilePath -> IO ()
+installFile top f = do
+ createDirectoryIfMissing True destdir
+ callProcess "cp" [f, destdir]
+ where
+ destdir = inTop top $ takeDirectory f
+
+checkExe :: FilePath -> IO Bool
+checkExe f
+ | ".so" `isSuffixOf` f = return False
+ | otherwise = checkFileExe <$> readProcess "file" ["-L", f] ""
+
+{- Check that file(1) thinks it's a Linux ELF executable, or possibly
+ - a shared library (a few executables like ssh appear as shared libraries). -}
+checkFileExe :: String -> Bool
+checkFileExe s = and
+ [ "ELF" `isInfixOf` s
+ , "executable" `isInfixOf` s || "shared object" `isInfixOf` s
+ ]
diff --git a/ControlWindow.hs b/ControlWindow.hs
index 4f806c9..c5a6be9 100644
--- a/ControlWindow.hs
+++ b/ControlWindow.hs
@@ -67,7 +67,7 @@ openControlWindow = do
ichan <- newTMChanIO
ochan <- newTMChanIO
_ <- async $ serveControlSocket soc ichan ochan
- myexe <- getExecutablePath
+ myexe <- getMyExe
mproc <- runInVirtualTerminal winDesc myexe ["--control"]
let cannotrun = do
putStrLn "You need to open another shell prompt, and run: debug-me --control"
@@ -83,6 +83,13 @@ openControlWindow = do
Left (Just ControlWindowOpened) -> return (ichan, ochan)
Left _ -> error "unexpected message from control process"
Right _ -> cannotrun
+
+
+-- | Get path to debug-me program.
+--
+-- The standalone bundle sets DEBUG_ME_EXE to the path to use.
+getMyExe :: IO FilePath
+getMyExe = maybe getExecutablePath return =<< lookupEnv "DEBUG_ME_EXE"
type Prompt = ()
type Response = String
diff --git a/Makefile b/Makefile
index ca07972..3244942 100644
--- a/Makefile
+++ b/Makefile
@@ -41,22 +41,44 @@ debug-me:
clean:
if [ "$(BUILDER)" != ./Setup ] && [ "$(BUILDER)" != cabal ]; then $(BUILDER) clean; fi
- rm -rf debug-me dist .stack-work Setup Setup.hi Setup.o
+ rm -rf dist .stack-work tmp
+ rm -f debig-me Build/LinuxMkLibs Setup
+ find . -name \*.o -exec rm {} \;
+ find . -name \*.hi -exec rm {} \;
install: install-files
useradd --system debug-me
- chmod 700 $(PREFIX)/var/log/debug-me
- chown debug-me:debug-me $(PREFIX)/var/log/debug-me
-
-install-files: debug-me
- install -d $(PREFIX)/var/log/debug-me
- install -d $(PREFIX)/usr/bin
- install -s -m 0755 debug-me $(PREFIX)/usr/bin/debug-me
- install -d $(PREFIX)/usr/share/man/man1
- install -m 0644 debug-me.1 $(PREFIX)/usr/share/man/man1/debug-me.1
- install -d $(PREFIX)/lib/systemd/system
- install -m 0644 debug-me.service $(PREFIX)/lib/systemd/system/debug-me.service
- install -d $(PREFIX)/etc/init.d
- install -m 0755 debug-me.init $(PREFIX)/etc/init.d/debug-me
- install -d $(PREFIX)/etc/default
- install -m 0644 debug-me.default $(PREFIX)/etc/default/debug-me
+ chmod 700 $(DESTDIR)$(PREFIX)/var/log/debug-me
+ chown debug-me:debug-me $(DESTDIR)$(PREFIX)/var/log/debug-me
+
+install-files: debug-me install-mans
+ install -d $(DESTDIR)$(PREFIX)/var/log/debug-me
+ install -d $(DESTDIR)$(PREFIX)/usr/bin
+ install -s -m 0755 debug-me $(DESTDIR)$(PREFIX)/usr/bin/debug-me
+ install -d $(DESTDIR)$(PREFIX)/lib/systemd/system
+ install -m 0644 debug-me.service $(DESTDIR)$(PREFIX)/lib/systemd/system/debug-me.service
+ install -d $(DESTDIR)$(PREFIX)/etc/init.d
+ install -m 0755 debug-me.init $(DESTDIR)$(PREFIX)/etc/init.d/debug-me
+ install -d $(DESTDIR)$(PREFIX)/etc/default
+ install -m 0644 debug-me.default $(DESTDIR)$(PREFIX)/etc/default/debug-me
+
+install-mans:
+ install -d $(DESTDIR)$(PREFIX)/usr/share/man/man1
+ install -m 0644 debug-me.1 $(DESTDIR)$(PREFIX)/usr/share/man/man1/debug-me.1
+
+Build/LinuxMkLibs: Build/LinuxMkLibs.hs
+ ghc --make $@ -Wall -fno-warn-tabs
+
+LINUXSTANDALONE_DEST=tmp/debug-me
+linuxstandalone: debug-me Build/LinuxMkLibs
+ rm -rf "$(LINUXSTANDALONE_DEST)"
+ mkdir -p tmp
+ cp -R standalone/linux/skel "$(LINUXSTANDALONE_DEST)"
+ install -d "$(LINUXSTANDALONE_DEST)/bin"
+ cp debug-me "$(LINUXSTANDALONE_DEST)/bin/"
+ strip "$(LINUXSTANDALONE_DEST)/bin/debug-me"
+ ./Build/LinuxMkLibs "$(LINUXSTANDALONE_DEST)"
+ $(MAKE) install-mans DESTDIR="$(LINUXSTANDALONE_DEST)"
+ cd tmp && tar c debug-me | gzip -9 --rsyncable > debug-me-standalone-$(shell dpkg --print-architecture).tar.gz
+
+.PHONY: debug-me
diff --git a/Utility/LinuxMkLibs.hs b/Utility/LinuxMkLibs.hs
new file mode 100644
index 0000000..5851ed1
--- /dev/null
+++ b/Utility/LinuxMkLibs.hs
@@ -0,0 +1,60 @@
+{- Linux library copier and binary shimmer
+ -
+ - Copyright 2013 Joey Hess <id@joeyh.name>
+ -
+ - License: BSD-2-clause
+ -}
+
+module Utility.LinuxMkLibs where
+
+import System.IO
+import System.Directory (doesFileExist)
+import Data.Maybe
+import System.FilePath
+import Data.List.Utils
+import System.Posix.Files
+import Data.Char
+import Control.Monad.IfElse
+import Control.Applicative
+import System.Process
+import Prelude
+
+{- Installs a library. If the library is a symlink to another file,
+ - install the file it links to, and update the symlink to be relative. -}
+installLib :: (FilePath -> FilePath -> IO ()) -> FilePath -> FilePath -> IO (Maybe FilePath)
+installLib installfile top lib = go =<< doesFileExist lib
+ where
+ go False = return Nothing
+ go True = do
+ installfile top lib
+ checksymlink lib
+ return $ Just $ takeDirectory lib
+
+ checksymlink f = whenM (isSymbolicLink <$> getSymbolicLinkStatus (inTop top f)) $ do
+ l <- readSymbolicLink (inTop top f)
+ let absl = takeDirectory f </> l
+ --target <- relPathDirToFile (takeDirectory f) absl
+ installfile top absl
+ --removeLink (top ++ f)
+ --createSymbolicLink target (inTop top f)
+ checksymlink absl
+
+-- Note that f is not relative, so cannot use </>
+inTop :: FilePath -> FilePath -> FilePath
+inTop top f = top ++ f
+
+{- Parse ldd output, getting all the libraries that the input files
+ - link to. Note that some of the libraries may not exist
+ - (eg, linux-vdso.so) -}
+parseLdd :: String -> [FilePath]
+parseLdd = concatMap (getlib . dropWhile isSpace) . lines
+ where
+ getlib = concatMap (take 1) . map words . take 1 . reverse . split " => "
+
+{- Get all glibc libs and other support files, including gconv files
+ -
+ - XXX Debian specific. -}
+glibcLibs :: IO [FilePath]
+glibcLibs = lines <$> readProcess "sh"
+ ["-c", "dpkg -L libc6:$(dpkg --print-architecture) libgcc1:$(dpkg --print-architecture) | egrep '\\.so|gconv'"]
+ mempty
diff --git a/standalone/linux/skel/README b/standalone/linux/skel/README
new file mode 100644
index 0000000..0126d8e
--- /dev/null
+++ b/standalone/linux/skel/README
@@ -0,0 +1,6 @@
+You can put this directory into your PATH, or symlink the programs in this
+directory to anyplace already in your PATH, and use debug-me the same
+as if you'd installed it using a package manager.
+
+This should work on any Linux system of the appropriate architecture.
+More or less.
diff --git a/standalone/linux/skel/runshell b/standalone/linux/skel/runshell
new file mode 100755
index 0000000..ca7dbe5
--- /dev/null
+++ b/standalone/linux/skel/runshell
@@ -0,0 +1,80 @@
+#!/bin/sh
+# Runs a shell command (or interactive shell) using the binaries and
+# libraries bundled with this app.
+
+set -e
+
+base="$(dirname "$0")"
+
+if [ ! -d "$base" ]; then
+ echo "** cannot find base directory (I seem to be $0)" >&2
+ exit 1
+fi
+
+if [ ! -e "$base/bin/debug-me" ]; then
+ echo "** base directory $base does not contain bin/debug-me" >&2
+ exit 1
+fi
+
+# Get absolute path to base, to avoid breakage when things change directories.
+orig="$(pwd)"
+cd "$base"
+base="$(pwd)"
+cd "$orig"
+
+# --library-path won't work if $base contains : or ;
+# Detect this problem, and work around it by using a temp directory.
+if echo "$base" | grep -q '[:;]'; then
+ tbase=$(mktemp -d -p /tmp debugmeshimXXXXXXXXX 2>/dev/null || true)
+ if [ -z "$tbase" ]; then
+ tbase="/tmp/debugmeshim.$$"
+ mkdir "$tbase"
+ fi
+ ln -s "$base" "$tbase/link"
+ base="$tbase/link"
+ cleanuptbase () {
+ rm -rf "$tbase"
+ }
+ trap cleanuptbase EXIT
+fi
+
+# Put our binaries first, to avoid issues with out of date or incompatible
+# system binaries. Extra binaries come after system path.
+ORIG_PATH="$PATH"
+export ORIG_PATH
+PATH="$base/bin:$PATH:$base/extra"
+export PATH
+
+# These env vars are used by the shim wrapper around each binary.
+for lib in $(cat "$base/libdirs"); do
+ DEBUG_ME_LD_LIBRARY_PATH="$base/$lib:$DEBUG_ME_LD_LIBRARY_PATH"
+done
+export DEBUG_ME_LD_LIBRARY_PATH
+DEBUG_ME_DIR="$base"
+export DEBUG_ME_DIR
+
+ORIG_GCONV_PATH="$GCONV_PATH"
+export ORIG_GCONV_PATH
+GCONV_PATH="$base/$(cat "$base/gconvdir")"
+export GCONV_PATH
+
+ORIG_MANPATH="$MANPATH"
+export ORIG_MANPATH
+MANPATH="$base/usr/share/man:$MANPATH"
+export MANPATH
+
+DEBUG_ME_EXE="$base/debug-me"
+export DEBUG_ME_EXE
+
+if [ "$1" ]; then
+ cmd="$1"
+ shift 1
+ if [ -z "$tbase" ]; then
+ exec "$cmd" "$@"
+ else
+ # allow EXIT trap to cleanup
+ "$cmd" "$@"
+ fi
+else
+ sh
+fi