Skip to content

Commit 79997f9

Browse files
authored
Merge pull request #27 from bluca/cpe_rpm
Add --cpe auto and --root options
2 parents 92a7283 + 682a06f commit 79997f9

11 files changed

Lines changed: 150 additions & 33 deletions

generate-package-notes.py

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,21 @@
5757

5858
import argparse
5959
import itertools
60+
import os
6061
import re
62+
from pathlib import Path
6163

6264
import simplejson as json
6365

6466
DOC_PARAGRAPHS = ['\n'.join(group)
6567
for (key, group) in itertools.groupby(__doc__.splitlines(), bool)
6668
if key]
6769

68-
def read_os_release(field):
70+
def read_os_release(field, root=Path('/')):
6971
try:
70-
f = open('/etc/os-release')
72+
f = open(root / 'etc/os-release')
7173
except FileNotFoundError:
72-
f = open('/usr/lib/os-release')
74+
f = open(root / 'usr/lib/os-release')
7375

7476
prefix = '{}='.format(field)
7577
for line in f:
@@ -108,30 +110,20 @@ def parse_args():
108110
p.add_argument('--package-architecture', metavar='ARCH',
109111
help='The code architecture of the binaries (e.g. arm64 or s390x)')
110112
p.add_argument('--cpe',
111-
help='NIST CPE identifier of the vendor operating system')
113+
help='NIST CPE identifier of the vendor operating system, or \'auto\' to parse from system-release-cpe or os-release')
112114
p.add_argument('--rpm', metavar='NEVRA',
113115
help='Extract type,name,version,architecture from a full rpm name')
114116
p.add_argument('--debug-info-url', metavar='URL',
115117
help='URL of the debuginfod server where sources can be queried')
116118
p.add_argument('--readonly', metavar='BOOL',
117119
type=str_to_bool, default=True,
118120
help='Make the notes section read-only (requires binutils 2.38)')
121+
p.add_argument('--root', metavar='PATH', type=Path, default="/",
122+
help='When a file (eg: /usr/lib/os-release) is parsed, open it relatively from this hierarchy')
119123
p.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
120124

121125
opts = p.parse_args()
122126

123-
if opts.cpe is None:
124-
opts.cpe = read_os_release('CPE_NAME')
125-
126-
if opts.rpm:
127-
split = re.match(r'(.*?)-([0-9].*)\.(.*)', opts.rpm)
128-
if not split:
129-
raise ValueError('{!r} does not seem to be a valid package name'.format(opts.rpm))
130-
opts.package_type = 'rpm'
131-
opts.package_name = split.group(1)
132-
opts.package_version = split.group(2)
133-
opts.package_architecture = split.group(3)
134-
135127
return opts
136128

137129
def encode_bytes(arr):
@@ -183,6 +175,24 @@ def json_serialize(s):
183175
separators=(',', ':'))
184176

185177
def gather_data(opts):
178+
if opts.cpe == 'auto':
179+
try:
180+
with open(Path(opts.root, 'usr/lib/system-release-cpe'), 'r') as f:
181+
opts.cpe = f.read()
182+
except FileNotFoundError:
183+
opts.cpe = read_os_release('CPE_NAME', root=opts.root)
184+
if opts.cpe is None or opts.cpe == "":
185+
raise ValueError(f"Could not read {opts.root}usr/lib/system-release-cpe or CPE_NAME from {opts.root}usr/lib/os-release")
186+
187+
if opts.rpm:
188+
split = re.match(r'(.*?)-([0-9].*)\.(.*)', opts.rpm)
189+
if not split:
190+
raise ValueError('{!r} does not seem to be a valid package name'.format(opts.rpm))
191+
opts.package_type = 'rpm'
192+
opts.package_name = split.group(1)
193+
opts.package_version = split.group(2)
194+
opts.package_architecture = split.group(3)
195+
186196
data = {
187197
'type': opts.package_type,
188198
'name': opts.package_name,
@@ -192,8 +202,8 @@ def gather_data(opts):
192202
if opts.cpe:
193203
data['osCpe'] = opts.cpe
194204
else:
195-
data['os'] = read_os_release('ID')
196-
data['osVersion'] = read_os_release('VERSION_ID')
205+
data['os'] = read_os_release('ID', root=opts.root)
206+
data['osVersion'] = read_os_release('VERSION_ID', root=opts.root)
197207
if opts.debug_info_url:
198208
data['debugInfoUrl'] = opts.debug_info_url
199209
return data

generate-package-notes.sh

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,16 @@
5454

5555
json=
5656
readonly="(READONLY) "
57+
root=
5758

5859
help() {
5960
echo "Usage: $0 [OPTION]..."
6061
echo "Generate a package notes linker script from specified metadata."
6162
echo
6263
echo " -h, --help display this help and exit"
6364
echo " --readonly BOOL whether to add the READONLY attribute to script (default: true)"
65+
echo " --root PATH when a file (eg: os-release) is parsed, open it relatively to this hierarchy (default: not set)"
66+
echo " --cpe VALUE NIST CPE identifier of the vendor operating system, or 'auto' to parse from system-release-cpe or os-release"
6467
echo " --package-type TYPE set the package type (e.g. 'rpm' or 'deb')"
6568
echo " --package-name NAME set the package name"
6669
echo " --package-version VERSION set the package version"
@@ -92,6 +95,8 @@ append_parameter() {
9295

9396
# Support the same fixed parameters as the python script
9497
parse_options() {
98+
cpe=
99+
95100
while :; do
96101
case $1 in
97102
-h|-\?|--help)
@@ -109,6 +114,13 @@ parse_options() {
109114
esac
110115
shift
111116
;;
117+
--root)
118+
if [ -z "${2}" ] || [ ! -d "${2}" ]; then
119+
invalid_argument "${1}"
120+
fi
121+
root="${2}"
122+
shift
123+
;;
112124
--package-type)
113125
append_parameter "type" "${2}"
114126
shift
@@ -126,7 +138,10 @@ parse_options() {
126138
shift
127139
;;
128140
--cpe)
129-
append_parameter "osCpe" "${2}"
141+
if [ -z "${2}" ]; then
142+
invalid_argument "${1}"
143+
fi
144+
cpe="${2}"
130145
shift
131146
;;
132147
--debug-info-url)
@@ -148,6 +163,26 @@ parse_options() {
148163
shift
149164
done
150165

166+
# Parse at the end, so that --root can be used in any position
167+
if [ "${cpe}" = "auto" ]; then
168+
if [ -r "${root}/usr/lib/system-release-cpe" ]; then
169+
cpe="$(cat "${root}/usr/lib/system-release-cpe")"
170+
elif [ -r "${root}/etc/os-release" ]; then
171+
# shellcheck disable=SC1090 disable=SC1091
172+
cpe="$(. "${root}/etc/os-release" && echo "${CPE_NAME}")"
173+
elif [ -r "${root}/usr/lib/os-release" ]; then
174+
# shellcheck disable=SC1090 disable=SC1091
175+
cpe="$(. "${root}/usr/lib/os-release" && echo "${CPE_NAME}")"
176+
fi
177+
if [ -z "${cpe}" ]; then
178+
printf 'ERROR: --cpe auto but cannot read %s/usr/lib/system-release-cpe or parse CPE_NAME from %s/etc/os-release or %s/usr/lib/os-release.\n' "${root}" "${root}" "${root}" >&2
179+
exit 1
180+
fi
181+
fi
182+
if [ -n "${cpe}" ]; then
183+
append_parameter "osCpe" "${cpe}"
184+
fi
185+
151186
# Terminate the JSON object
152187
if [ -n "${json}" ]; then
153188
json="${json}}"
@@ -235,8 +270,8 @@ write_script() {
235270

236271
if ! parse_options "$@" && [ "$#" -gt 0 ]; then
237272
# Not supported on every distro
238-
if [ -r /usr/lib/system-release-cpe ]; then
239-
cpe="$(cat /usr/lib/system-release-cpe)"
273+
if [ -r "${root}/usr/lib/system-release-cpe" ]; then
274+
cpe="$(cat "${root}/usr/lib/system-release-cpe")"
240275
json_cpe=",\"osCpe\":\"${cpe}\""
241276
fi
242277

man/generate-package-notes.1

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,20 @@ to
8181
This defaults to the empty string, but for Debian packages it should
8282
be set to the Debian architecture identifier of the binary.
8383
.TP
84+
.TP
85+
.BI \-\-root= PATH
86+
When reading files, for example /usr/lib/system-release-cpe and /usr/lib/os-release,
87+
open them relatively to the specified directory.
88+
.TP
8489
.BI \-\-cpe= CPE
8590
Set the key
8691
.I osCpe
8792
to
8893
.IR CPE .
89-
This defaults to the value of the
90-
.I CPE_NAME
91-
field in the first of
92-
.I /etc/os\-release
93-
or
94-
.I /usr/lib/os\-release
95-
that exists.
96-
Otherwise the key
97-
.I osCpe
98-
is omitted in the note.
94+
If the special value
95+
.I auto
96+
is passed, then the content of the /usr/lib/system-release-cpe file, or the value of CPE_NAME
97+
from os-release if the former was not found, will be used.
9998
.TP
10099
.BI \-\-rpm= PACKAGE \- VERSION
101100
Set the keys

rpm/macros.package-notes-srpm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,4 @@
5454
# The command to actually generate the linker script that inserts the
5555
# notes file. This command is automatically used as part of the build
5656
# preamble.
57-
%_generate_package_note_file %[%_package_note_status?"if [ -f %{_rpmconfigdir}/generate-package-notes.sh ]; then %{_rpmconfigdir}/generate-package-notes.sh %[0%{?_package_note_readonly}?"":"--readonly no "]--package-name ${RPM_PACKAGE_NAME:?} --package-version ${RPM_PACKAGE_VERSION:?}-${RPM_PACKAGE_RELEASE:?} --package-architecture ${RPM_ARCH:?} >%{_package_note_file}; fi":""]
57+
%_generate_package_note_file %[%_package_note_status?"if [ -f %{_rpmconfigdir}/generate-package-notes.sh ]; then %{_rpmconfigdir}/generate-package-notes.sh %[0%{?_package_note_readonly}?"":"--readonly no "]--package-name ${RPM_PACKAGE_NAME:?} --package-version ${RPM_PACKAGE_VERSION:?}-${RPM_PACKAGE_RELEASE:?} --package-architecture ${RPM_ARCH:?} --cpe auto >%{_package_note_file}; fi":""]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
SECTIONS
2+
{
3+
.note.package (READONLY) : ALIGN(4) {
4+
BYTE(0x04) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Owner including NUL */
5+
BYTE(0x38) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Value including NUL */
6+
BYTE(0x7e) BYTE(0x1a) BYTE(0xfe) BYTE(0xca) /* Note ID */
7+
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
8+
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","osCpe":"cpe:/o:fedoraproject:fedora:34"}\x00' */
9+
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
10+
BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
11+
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
12+
BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
13+
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x63)
14+
BYTE(0x70) BYTE(0x65) BYTE(0x3a) BYTE(0x2f)
15+
BYTE(0x6f) BYTE(0x3a) BYTE(0x66) BYTE(0x65)
16+
BYTE(0x64) BYTE(0x6f) BYTE(0x72) BYTE(0x61)
17+
BYTE(0x70) BYTE(0x72) BYTE(0x6f) BYTE(0x6a)
18+
BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x3a)
19+
BYTE(0x66) BYTE(0x65) BYTE(0x64) BYTE(0x6f)
20+
BYTE(0x72) BYTE(0x61) BYTE(0x3a) BYTE(0x33)
21+
BYTE(0x34) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
22+
}
23+
}
24+
INSERT AFTER .note.gnu.build-id;
25+
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
SECTIONS
2+
{
3+
.note.package (READONLY) : ALIGN(4) {
4+
BYTE(0x04) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Owner including NUL */
5+
BYTE(0x38) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Value including NUL */
6+
BYTE(0x7e) BYTE(0x1a) BYTE(0xfe) BYTE(0xca) /* Note ID */
7+
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
8+
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","osCpe":"cpe:/o:fedoraproject:fedora:33"}\x00' */
9+
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
10+
BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
11+
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
12+
BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
13+
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x63)
14+
BYTE(0x70) BYTE(0x65) BYTE(0x3a) BYTE(0x2f)
15+
BYTE(0x6f) BYTE(0x3a) BYTE(0x66) BYTE(0x65)
16+
BYTE(0x64) BYTE(0x6f) BYTE(0x72) BYTE(0x61)
17+
BYTE(0x70) BYTE(0x72) BYTE(0x6f) BYTE(0x6a)
18+
BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x3a)
19+
BYTE(0x66) BYTE(0x65) BYTE(0x64) BYTE(0x6f)
20+
BYTE(0x72) BYTE(0x61) BYTE(0x3a) BYTE(0x33)
21+
BYTE(0x33) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
22+
}
23+
}
24+
INSERT AFTER .note.gnu.build-id;
25+
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CPE_NAME="cpe:/o:fedoraproject:fedora:34"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ID=fedora
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cpe:/o:fedoraproject:fedora:33

tests/test_basics.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
import sys
44
from importlib import resources
5+
from pathlib import Path
56

6-
from _generate_package_notes import generate_section
7+
from _generate_package_notes import gather_data, generate_section
78

89

10+
class dict_dot(dict):
11+
__getattr__ = dict.get
12+
913
def test_fedora_package():
1014
input = dict(type='rpm', name='package', version='1.2.3', architecture='noarch', osCpe='CPE')
1115
text = '\n'.join(generate_section(input))
@@ -34,3 +38,17 @@ def test_fedora_long_name():
3438
text = '\n'.join(generate_section(input))
3539
expected = resources.read_text('resources', 'fedora-long-name.ld')
3640
assert text == expected[:-1]
41+
42+
def test_auto_cpe_system_release():
43+
opts = dict_dot(package_type='rpm', cpe='auto', root=Path(__file__).absolute().parent / 'resources/root/')
44+
input = gather_data(opts)
45+
text = '\n'.join(generate_section(input))
46+
expected = resources.read_text('resources', 'fedora-cpe-system-release.ld')
47+
assert text == expected[:-1]
48+
49+
def test_auto_cpe_os_release():
50+
opts = dict_dot(package_type='rpm', cpe='auto', root=Path(__file__).absolute().parent / 'resources/root-no-cpe/')
51+
input = gather_data(opts)
52+
text = '\n'.join(generate_section(input))
53+
expected = resources.read_text('resources', 'fedora-cpe-os-release.ld')
54+
assert text == expected[:-1]

0 commit comments

Comments
 (0)