-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathrpm2pkg
307 lines (269 loc) · 11.5 KB
/
rpm2pkg
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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#!/bin/bash
#
# rpm2pkg - creating Solaris SVR4 pkg files from Linux rpm's
# --bash rewrite of Mike Golvach's original perl script
#
# 2015 - Rawiri Blundell
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
###############################################################################
# Force PATH to start with xpg4. This ensures we don't use Solaris oawk without
# needing to go to the expense of checking for xpg4-oawk/gawk/nawk/mawk
# xpg4 awk is important for the generation of the prototype file
PATH=/usr/xpg4/bin:$PATH
export PATH
# Check we have the required binaries
ErrCount=0
for Prog in cpio pkgchk pkgproto pkgmk pkgtrans rpm rpm2cpio; do
if ! command -v "${Prog}" &>/dev/null; then
printf "%s\n" "[ERROR]: rpm2pkg requires ${Prog}, please install it. This may require the heirloom suite."
ErrCount=$(( ErrCount + 1 ))
fi
done
# If any packages are missing, then ErrCount's going
# to be greater than 0, in which case, exit.
if [[ "${ErrCount}" -gt 0 ]]; then
exit 1
fi
# Function for help output
Fn_Usage() {
printf "%s\n" "rpm2pkg - a tool to convert rpm's to SVR4 Solaris pkg format"\
"" \
"[Usage]: rpm2pkg -[abhipru] filename.rpm" \
" -a, 'after-install' i.e. path to postinstall script" \
" -b, 'before-install' i.e. path to preinstall script" \
" -h, help" \
" -i, interrupt. This gives a pause before final packaging to allow manual changes" \
" -p, manually define PKG attr. Default is RPMC<name of rpm package>" \
" -r, 'remove-install' i.e. path to post-uninstall script" \
" -u, 'uninstall' i.e. path to pre-uninstall script" \
"" \
"By default, 'rpm2pkg' will search for package scripts in the input rpm " \
"and simply copy them over. There is no guarantee that these scripts will work" \
"on the Solaris target system, so you may want to provide your own." \
"" \
"When defining the PKG attribute, it's common to use 3-4 uppercase characters" \
"usually an abbreviated company or repo name e.g. SUNWsomepkg. 'rpm2pkg' will" \
"default to 'RPMC<name gathered from the rpm>', where RPMC = RPM Converted." \
"" \
"There are many other aspects to a package that this tool can't reasonably cater for" \
"so '-i' is provided for you to make any final tweaks and customisations prior" \
"to the final packaging e.g. adding/removing files, adjusting pkgmap etc"
}
# GETOPTS
###############################################################################
while getopts ":a:b:hip:r:u" Flags; do
case "${Flags}" in
a) PostInst="${OPTARG}";;
b) PreInst="${OPTARG}";;
h) Fn_Usage
exit 0;;
i) PauseBuild=true;;
p) rpmPkgName="${OPTARG}";;
r) PostUninst="${OPTARG}";;
u) PreUninst="${OPTARG}";;
\?) printf "%s\n" "[ERROR]: Invalid option: $OPTARG. Try '${0##*/} -h' for usage." >&2
exit 1;;
:) printf "%s\n" "[ERROR]: Option '-${OPTARG}' requires an argument." >&2
exit 1;;
esac
done
# Shift optind, allowing us to get the rpm filename without needing getopts
shift "$(( OPTIND - 1 ))"
###############################################################################
# Get the location of our rpm
OrigRPM=$1
# Check that we actually have a parameter
if [[ ! $# -eq 1 ]]; then
Fn_Usage
exit 1
fi
# Check that we actually have an rpm
if ! file "${OrigRPM}" | grep RPM &>/dev/null; then
printf "%s\n" "[ERROR]: '${OrigRPM}' does not appear to be an rpm..."
exit 1
fi
# Cater for rpm pathing. This allows relative and absolute pathing
# by translating OrigRPM into a full pathname
OrigRPM="$(cd "$(dirname "${OrigRPM}")" && pwd)/$(basename "${OrigRPM}")"
###############################################################################
# This mktemp command tries GNU mktemp style first, then fails over to BSD style
TmpDir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')
# Remove the TmpDir whichever way we exit
trap 'rm -rf "${TmpDir}"' 0 1 2 3 15
###############################################################################
# The original perl script tried to use 'strings [rpm package]' to dump
# information into an array which it then parsed. Trying this against various rpm's
# showed me that this was probably not a reliable approach.
# We can use 'rpm' to pull the required information straight from the package file.
rpmName=$(rpm -qp --queryformat '%{NAME}\n' "${OrigRPM}")
rpmVer=$(rpm -qp --queryformat '%{VERSION}\n' "${OrigRPM}")
rpmArch=$(rpm -qp --queryformat '%{ARCH}\n' "${OrigRPM}")
rpmVendor=$(rpm -qp --queryformat '%{VENDOR}\n' "${OrigRPM}")
rpmPkger=$(rpm -qp --queryformat '%{PACKAGER}\n' "${OrigRPM}")
rpmDesc=$(rpm -qp --queryformat '%{DESCRIPTION}\n' "${OrigRPM}")
rpmBuildHost="$(rpm -qp --queryformat '%{BUILDHOST}\n' "${OrigRPM}")-$(date +%Y%m%d%H%M)"
# These often default to root, but just in case
rpmFileOwner=$(rpm -qp --queryformat '%{FILEUSERNAME}\n' "${OrigRPM}" 2>/dev/null)
rpmFileGroup=$(rpm -qp --queryformat '%{FILEGROUPNAME}\n' "${OrigRPM}" 2>/dev/null)
# Try to figure out the source architecture
if [[ "${rpmArch}" = noarch ]]; then
rpmArch=all
elif [[ "${rpmArch}" = i386 ]]; then
rpmArch=i386
elif [[ "${rpmArch}" = x86_64 ]]; then
rpmArch=all
else
printf "%s\n" "[WARN]: The target architecture could not be gathered from the rpm." \
"Setting to 'all' and hoping for the best..."
rpmArch=all
fi
# Next, handle the PKG attribute
if [[ -z "${rpmPkgName}" ]]; then
# Auto convert to RPMCrpmName (RPMC = RPM Converted)
rpmPkgName="RPMC${rpmName}"
# If rpmPkgName is more than 32 chars, we prompt the user to shorten it
# If we exceed our limit, we need to ask the user to provide a name
# Note: In older versions of Solaris, this was a 9 or 10 char limit depending on who you asked.
while [[ "${#rpmPkgName}" -gt 32 ]]; do
printf "%s\n" "[INFO]: Solaris PKG attribute must be less than 32 chars, but RPM name attribute is ${#rpmPkgName} chars long"
read -rep $'Please enter a suitable abbreviated PKG name e.g. RPMCmypkg: ' rpmPkgName
printf "%s\n" ""
done
fi
# Figure out the package scripts
###############################################################################
# If the preinstall variable is in use...
if [[ -n "${PreInst}" ]]; then
# Validate that it's a file we can get to. If so, copy it
if [[ -f "${PreInst}" ]]; then
cp "${PreInst}" "${TmpDir}"/preinstall
printf "%s\n" "[INFO]: Copied pre-install script from ${PreInst} to '${TmpDir}/preinstall'"
# Otherwise, error out.
else
printf "%s\n" "[ERROR]: '${PreInst}' does not appear to exist"
exit 1
fi
# If the preinstall variable isn't in use, check the rpm for a preinstall script
# If it's found, copy it over. Notify the user of this as well.
else
if ! rpm -qp --queryformat '%{PREIN}\n' "${OrigRPM}" | grep "(none)" &>/dev/null; then
rpm -qp --queryformat '%{PREIN}\n' "${OrigRPM}" > "${TmpDir}"/preinstall
printf "%s\n" "[INFO]: Copied pre-install script from ${OrigRPM} to '${TmpDir}/preinstall'"
fi
fi
# ...the same logic as above except for the respective scripts:
# Post-install
if [[ -n "${PostInst}" ]]; then
if [[ -f "${PostInst}" ]]; then
cp "${PostInst}" "${TmpDir}"/postinstall
printf "%s\n" "[INFO]: Copied post-install script from '${PostInst}' to '${TmpDir}/postinstall'"
else
printf "%s\n" "[ERROR]: '${PostInst}' does not appear to exist"
exit 1
fi
else
if ! rpm -qp --queryformat '%{POSTIN}\n' "${OrigRPM}" | grep "(none)" &>/dev/null; then
rpm -qp --queryformat '%{POSTIN}\n' "${OrigRPM}" > "${TmpDir}"/postinstall
printf "%s\n" "[INFO]: Copied post-install script from ${OrigRPM} to '${TmpDir}/postinstall'"
fi
fi
# Pre-uninstall
if [[ -n "${PreUninst}" ]]; then
if [[ -f "${PreUninst}" ]]; then
cp "${PreUninst}" "${TmpDir}"/preremove
printf "%s\n" "[INFO]: Copied pre-uninstall script from '${PreUninst}' to '${TmpDir}/preremove'"
else
printf "%s\n" "[ERROR]: '${PreUninst}' does not appear to exist"
exit 1
fi
else
if ! rpm -qp --queryformat '%{PREUN}\n' "${OrigRPM}" | grep "(none)" &>/dev/null; then
rpm -qp --queryformat '%{PREUN}\n' "${OrigRPM}" > "${TmpDir}"/preremove
printf "%s\n" "[INFO]: Copied pre-uninstall script from ${OrigRPM} to '${TmpDir}/preremove'"
fi
fi
# Post-uninstall
if [[ -n "${PostUninst}" ]]; then
if [[ -f "${PostUninst}" ]]; then
cp "${PostUninst}" "${TmpDir}"/postremove
printf "%s\n" "[INFO]: Copied post-uninstall script from '${PostUninst}' to '${TmpDir}/postremove'"
else
printf "%s\n" "[ERROR]: '${PostUninst}' does not appear to exist"
exit 1
fi
else
if ! rpm -qp --queryformat '%{POSTUN}\n' "${OrigRPM}" | grep "(none)" &>/dev/null; then
rpm -qp --queryformat '%{POSTUN}\n' "${OrigRPM}" > "${TmpDir}"/postremove
printf "%s\n" "[INFO]: Copied post-uninstall script from ${OrigRPM} to '${TmpDir}/postremove'"
fi
fi
###############################################################################
# Start a subshell, cd to the TmpDir
(
cd "${TmpDir}" || exit 1
# Convert the rpm to cpio, then pipe that to cpio
# cpio -d (make dirs) -i (extract) -m (keep perms)
rpm2cpio "${OrigRPM}" | cpio -dim &>/dev/null
# Generate the prototype file
{
printf "%s\n" "i pkginfo"
if [[ -f "${TmpDir}"/preinstall ]]; then
printf "%s\n" "i preinstall"
fi
if [[ -f "${TmpDir}"/postinstall ]]; then
printf "%s\n" "i postinstall"
fi
if [[ -f "${TmpDir}"/preremove ]]; then
printf "%s\n" "i preremove"
fi
if [[ -f "${TmpDir}"/postremove ]]; then
printf "%s\n" "i postremove"
fi
# Add the default ownership for all files. This seems to be ignored, but just in case...
printf "%s\n" "!default 0640 ${rpmFileOwner} ${rpmFileGroup}"
# Now generate a list using 'pkgproto' to format it. Because this has been finnicky
# we use 'awk' to set ownership in the prototype file. This is one reason for the -i option:
# to allow fine tuning of file permissions/mapping in the prototype file.
find . -print | pkgproto \
| egrep -v 'none prototype|none preinstall|none postinstall|none preremove|none postremove' \
| awk -v rpmF="${rpmFileOwner}" -v rpmG="${rpmFileGroup}" '{$5=rpmF; $6=rpmG; print}'
} > "${TmpDir}"/prototype
# Leave the subshell
)
# Create the pkginfo file. Required fields: PKG, NAME, ARCH, VERSION, and CATEGORY
printf "%s\n" \
"PKG=${rpmPkgName}" \
"NAME=${rpmName}" \
"VERSION=${rpmVer}" \
"VENDOR=${rpmVendor}" \
"ARCH=${rpmArch}" \
"EMAIL=${rpmPkger}" \
"CATEGORY=application" \
"BASEDIR=/" \
"DESC=${rpmDesc}" \
"PSTAMP=${rpmBuildHost}" \
"CLASSES=none" >> "${TmpDir}"/pkginfo
# If a pause is requested, let's do so now.
if [[ "${PauseBuild}" = true ]]; then
printf "%s\n" "[INFO]: If you want to make any manual customisations to the code " \
"before it's packaged please do so now in ${TmpDir}. When you are done, hit Enter..."
read -rp "Press Enter to continue..."
fi
# Print a non-alcoholic bar
printf "%s\n" "================================================================================"
# Generate the package source
pkgmk -o -f "${TmpDir}/prototype" -b "${TmpDir}" -d /tmp || exit 1
# Print a non-alcoholic bar
printf "%s\n" "================================================================================"
# Validate it
pkgchk -d /tmp "${rpmPkgName}" || exit 1
# Print a non-alcoholic bar
printf "%s\n" "================================================================================"
# Build the pkg file
pkgtrans -o /tmp "${PWD}/${rpmName}.${rpmVer}.pkg" "${rpmPkgName}"
# And that's it
printf "%s\n" "================================================================================" \
"" \
"[INFO]: rpm2pkg has completed converting '${OrigRPM}' to '${PWD}/${rpmName}.${rpmVer}.pkg'" \
""