-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcopy-bin.sh
More file actions
executable file
·282 lines (234 loc) · 6.65 KB
/
Copy pathcopy-bin.sh
File metadata and controls
executable file
·282 lines (234 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#!/usr/bin/env sh
## GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
## DESCRIPTION
## This script is meant for copying binaries into distroless Docker images.
## It will copy binaries, shared object linked libraries the binary depends
## on, and it can additionally copy all symlinks pointing to the binary or
## shared object dependencies
## EXAMPLE USAGE
## Copy to a --prefix base a binary and its shared library linked dependencies
## (--ldd) and ensure that all symlinks pointing to binary (--bin) are also
## copied if those links are contained within a linked path (--links)
##
## copy-bin.sh --prefix /base \
## --ldd /bin/busybox \
## --bin /bin/busybox \
## --links /bin \
## --links /sbin \
## --links /usr/bin \
## --links /usr/sbin
##
## Alternate copy-bin.sh command doing the same thing
##
## copy-bin.sh --prefix /base \
## --ldd /bin/busybox \
## --links /bin:/sbin:/usr/bin:/usr/sbin
## EXIT CODES
## exit code 5
## Help text was shown
## exit code 127
## Utility not found; you must install it because this script depends on it
##
## FUNCTIONS
##
## Stderror output
stderr() {
echo "$@" >&2
}
## Verify app on exists
checkutil() {
if ! type "$1" >/dev/null; then
type "$1" >&2
fi
}
## Only copy if nothing exists
cp_lite() {
if [ -e "$prefix$1" ]; then
return
fi
_cp_lite_basepath="$(dirname "$1")"
mkdir -p "$prefix""$_cp_lite_basepath"
cp -a "$1" "$prefix""$1"
}
## Help stderr output
help() {
stderr '
DESCRIPTION
A utility for copying binaries and shared object dependencies
SYNOPSIS
copy-bin.sh --prefix PREFIX --ldd LDD [--bin BIN [[--links LINK] ...]]
copy-bin.sh -p PREFIX -l LDD [-b BIN [[-L LINK] ...]]
OPTIONS
--prefix PREFIX, -p PREFIX
The destination to copy LDD files and bin symlinks
--ldd LDD, -l LDD
Read the binary at path LDD, copy it to PREFIX; run ldd utility on
it and copy all shared object dependencies to PREFIX
--bin BIN, -b BIN
Symlinks should match destination bin. (does not perform copy)
Defaults to LDD if not specified and --links is specified
--links LINK, -L LINK
Copy symlink LINK to PREFIX if it points to BIN. This option can
be provided multiple times or colon:separated:paths provided
'
}
## Parse received args
parse_args() {
while [ "$#" -gt 0 ]; do
case "$1" in
--help | -h)
help
exit 5
;;
--prefix | -p)
prefix="$2"
shift
shift
;;
--ldd | -l)
ldd="$2"
shift
shift
;;
--bin | -b)
bin="$2"
shift
shift
;;
--links | -L)
if [ "$links" = "" ]; then
links="$2"
else
links="$links:$2"
fi
shift
shift
;;
*)
shift
;;
esac
done
if [ ! "$ldd" = "" ] && [ "$bin" = "" ] && [ ! "$links" = "" ]; then
bin="$ldd"
fi
}
## Validate received arguments
validate_args() {
_validate_args_errcode=0
if [ "$prefix" = "" ]; then
stderr "[timestamp: $(date +%F' '%T)] [level: ERROR] --prefix is required but not provided"
_validate_args_errcode=5
elif ! echo "$prefix" | grep '^/' >/dev/null; then
stderr "[timestamp: $(date +%F' '%T)] [level: ERROR] --prefix must be a full path and not relative"
_validate_args_errcode=5
fi
if [ ! "$ldd" = "" ]; then
if [ ! -f "$ldd" ]; then
stderr "[timestamp: $(date +%F' '%T)] [level: ERROR] --ldd must point to a regular file"
_validate_args_errcode=5
fi
fi
if [ ! "$links" = "" ]; then
if [ "$bin" = "" ]; then
stderr "[timestamp: $(date +%F' '%T)] [level: ERROR] --links were specified but --bin option not provided"
_validate_args_errcode=5
fi
fi
if [ ! "$_validate_args_errcode" = 0 ]; then
stderr "[timestamp: $(date +%F' '%T)] [level: WARNING] view source of this script at: $(type "$0") or see 'copy-bin.sh --help' for details"
fi
return "$_validate_args_errcode"
}
## Reading links and turning them into absolute paths
deref_symlink() {
_deref_symlink_deref="$(readlink "$1")"
if [ "$_deref_symlink_deref" = "" ]; then
return 1
fi
if ! echo "$_deref_symlink_deref" | grep '^/' >/dev/null; then
_deref_symlink_basepath="$(dirname "$1")"
_deref_symlink_deref="$_deref_symlink_basepath/$_deref_symlink_deref"
fi
if [ ! -e "$_deref_symlink_deref" ]; then
return 1
fi
echo "$_deref_symlink_deref"
}
## Copy links path
copy_links() {
if [ "$bin" = "" ]; then
return
fi
_copy_links_deref=""
echo "$links" | tr : '\n' | while read -r linkpath; do
find "$linkpath" -maxdepth 1 -type l | while read -r linkfile; do
## Deref link or ignore dead links
_copy_links_deref="$(deref_symlink "$linkfile")" || continue
if [ ! "$bin" = "$_copy_links_deref" ]; then
continue
fi
cp_lite "$linkfile"
done
done
}
## Trim spaces and sum, we leave only the paths to the libraries
parse_ldd() {
awk 'NF == 2 && $1 ~ /^\// { print $1; next }; NF == 4 && $3 ~ /^\// { print $3 }' | sort -u
}
## Copying relative links and actual files to absolute paths
copy_with_links() {
_copy_with_links_file="$1"
_copy_with_links_recursion_limit=100
_copy_with_links_rcount=0
## Keep dereferencing recursively and copy destination links
while [ ! "$_copy_with_links_file" = "" ]; do
cp_lite "$_copy_with_links_file"
if _copy_with_links_file="$(deref_symlink "$_copy_with_links_file")"; then
_copy_with_links_rcount="$((_copy_with_links_rcount + 1))"
if [ "$_copy_with_links_rcount" -gt "$_copy_with_links_recursion_limit" ]; then
stderr "[timestamp: $(date +%F' '%T)] [level: ERROR] too many links, recursed into $_copy_with_links_recursion_limit symlinks"
exit 1
fi
continue
fi
unset _copy_with_links_file
done
}
## Check ldd and launch copy method
copy_ldd() {
if [ "$ldd" = "" ]; then
return
fi
ldd "$ldd" 2>/dev/null | parse_ldd | while read -r file; do
if [ "$file" = "" ]; then
continue
fi
copy_with_links "$file"
done
cp_lite "$ldd"
}
##
## MAIN SCRIPT
##
## Exit on first error
set -e
main() {
## Pre-flight check for dependent utilities
for i in awk cp dirname find grep ldd mkdir readlink sort tr xargs; do
checkutil "$i"
done
## Parse script arguments
# prefix=""
# ldd=""
# bin=""
# links=""
parse_args "$@"
validate_args
## Create directory if not exists
[ -d "$prefix" ] || mkdir -p "$prefix"
copy_ldd
copy_links
}
## Launch entrypoint
main "$@"