diff options
| author | Michael Biebl <biebl@debian.org> | 2024-05-05 00:07:30 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2024-05-05 00:07:30 +0200 |
| commit | 34bb501be08aa2b313d88e67d6e0a7e0a3f9cfa6 (patch) | |
| tree | 4e6220877828be4c6f261de09ec0cb2d80e32389 /contrib | |
| parent | bba2e4b4de668db525cbfdfc35292e5a0b51671a (diff) | |
New upstream version 1.47.90 upstream/1.47.90
Diffstat (limited to 'contrib')
55 files changed, 10190 insertions, 0 deletions
diff --git a/contrib/alpine/REQUIRED_PACKAGES b/contrib/alpine/REQUIRED_PACKAGES new file mode 100755 index 00000000..1b299b2e --- /dev/null +++ b/contrib/alpine/REQUIRED_PACKAGES @@ -0,0 +1,53 @@ +#!/bin/sh + +set -ex + +apk update + +apk add \ + 'alpine-sdk' \ + 'autoconf' \ + 'automake' \ + 'bash' \ + 'clang' \ + 'curl-dev' \ + 'dbus' \ + 'dbus-glib-dev' \ + 'elogind-dev' \ + 'eudev-dev' \ + 'gcc' \ + 'git' \ + 'gnutls-dev' \ + 'gobject-introspection-dev' \ + 'gtk-doc' \ + 'intltool' \ + 'iproute2' \ + 'iptables' \ + 'jansson-dev' \ + 'libgudev-dev' \ + 'libndp-dev' \ + 'libnl3-dev' \ + 'libpsl-dev' \ + 'libsoup-dev' \ + 'libteam-dev' \ + 'libtool' \ + 'linux-headers' \ + 'make' \ + 'meson' \ + 'mobile-broadband-provider-info' \ + 'modemmanager-dev' \ + 'musl-dev' \ + 'newt-dev' \ + 'nss-dev' \ + 'polkit-dev' \ + 'ppp' \ + 'ppp-dev' \ + 'py3-dbus' \ + 'py3-gobject3' \ + 'py3-pexpect' \ + 'python3' \ + 'readline-dev' \ + 'util-linux-dev' \ + 'vala' \ + 'valgrind' \ + 'vim' diff --git a/contrib/art/logo/alternate/nm_logo_red.svg b/contrib/art/logo/alternate/nm_logo_red.svg new file mode 100644 index 00000000..50c26b70 --- /dev/null +++ b/contrib/art/logo/alternate/nm_logo_red.svg @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="50mm" + height="50mm" + viewBox="0 0 49.999999 49.999999" + version="1.1" + id="svg3790" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_logo_red.svg"> + <defs + id="defs3784" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.8" + inkscape:cx="89.730775" + inkscape:cy="89.762394" + inkscape:document-units="mm" + inkscape:current-layer="logo" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1" /> + <metadata + id="metadata3787"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="layer01" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-87.115936,-83.441986)"> + <g + id="logo" + transform="matrix(0.01396314,0,0,0.01396314,18.403102,71.599589)" + style="stroke-width:0.58136749" + inkscape:label="logo"> + <path + sodipodi:nodetypes="ccccccccccccccccccccccccc" + style="fill:#cc0000;fill-opacity:1;stroke:none;stroke-width:0.77839899" + d="M 4921.0159,848.11845 V 4427.6574 h 698.5017 V 2014.3905 l 513.5931,516.0984 -242.4055,243.5876 678.4845,681.8654 242.75,-244.6281 1210.4382,1216.3436 1.3107,1.3175 1.3117,-1.3175 h 476.8721 V 848.12395 H 8023.6883 L 6935.0748,1942.0493 6826.9864,1833.2256 6612.6048,2048.6539 5429.8454,848.11845 Z M 7824.6995,2011.8244 v 1253.4504 l -533.539,-536.1426 213.0014,-214.0412 -90.0745,-90.7209 z" + id="path1482" + inkscape:connector-curvature="0" + inkscape:label="cable" /> + <path + sodipodi:nodetypes="ccccccccccccccccccccc" + style="fill:#ffffff;stroke:none;stroke-width:0.77839899" + d="m 6828.2981,2003.7092 -765.3151,769.0494 88.9985,91.2718 203.6838,-204.6779 34.7174,36.2748 36.0988,34.8874 -203.683,204.6775 63.4313,63.7406 202.9925,-203.9839 34.7884,36.2051 36.0279,34.8878 -193.8409,211.4097 57.2489,57.5297 203.0644,-203.984 34.7166,36.2053 36.0304,34.9569 -202.9934,203.984 77.6182,77.997 765.3149,-769.0494 -255.1047,-255.0319 z" + id="path1498" + inkscape:connector-curvature="0" + inkscape:label="connector" /> + </g> + </g> +</svg> diff --git a/contrib/art/logo/alternate/nm_logo_red_reverse.svg b/contrib/art/logo/alternate/nm_logo_red_reverse.svg new file mode 100644 index 00000000..a3e95044 --- /dev/null +++ b/contrib/art/logo/alternate/nm_logo_red_reverse.svg @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="50mm" + height="50mm" + viewBox="0 0 50 50" + version="1.1" + id="svg18" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_logo_red_reverse.svg" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96"> + <defs + id="defs12" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.979899" + inkscape:cx="199.00923" + inkscape:cy="55.594808" + inkscape:document-units="mm" + inkscape:current-layer="logo" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1" /> + <metadata + id="metadata15"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-53.113205,-106.86279)"> + <g + id="g13"> + <rect + y="106.86279" + x="53.113205" + height="50" + width="50" + id="rect22" + style="fill:#cc0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.37634408;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <g + id="logo" + transform="matrix(0.01061199,0,0,0.01061199,6.8914335,103.86255)" + style="stroke-width:0.58136749" + inkscape:label="logo"> + <path + sodipodi:nodetypes="ccccccccccccccccccccccccc" + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.77839899" + d="M 4921.0159,848.11845 V 4427.6574 h 698.5017 V 2014.3905 l 513.5931,516.0984 -242.4055,243.5876 678.4845,681.8654 242.75,-244.6281 1210.4382,1216.3436 1.3107,1.3175 1.3117,-1.3175 h 476.8721 V 848.12395 H 8023.6883 L 6935.0748,1942.0493 6826.9864,1833.2256 6612.6048,2048.6539 5429.8454,848.11845 Z M 7824.6995,2011.8244 v 1253.4504 l -533.539,-536.1426 213.0014,-214.0412 -90.0745,-90.7209 z" + id="path1482" + inkscape:connector-curvature="0" + inkscape:label="cable" /> + <path + sodipodi:nodetypes="ccccccccccccccccccccc" + style="fill:#cc0000;fill-opacity:1;stroke:none;stroke-width:0.77839899" + d="m 6828.2981,2003.7092 -765.3151,769.0494 88.9985,91.2718 203.6838,-204.6779 34.7174,36.2748 36.0988,34.8874 -203.683,204.6775 63.4313,63.7406 202.9925,-203.9839 34.7884,36.2051 36.0279,34.8878 -193.8409,211.4097 57.2489,57.5297 203.0644,-203.984 34.7166,36.2053 36.0304,34.9569 -202.9934,203.984 77.6182,77.997 765.3149,-769.0494 -255.1047,-255.0319 z" + id="path1498" + inkscape:connector-curvature="0" + inkscape:label="connector" /> + </g> + </g> + </g> +</svg> diff --git a/contrib/art/logo/alternate/nm_logo_reverse.svg b/contrib/art/logo/alternate/nm_logo_reverse.svg new file mode 100644 index 00000000..0f863ea2 --- /dev/null +++ b/contrib/art/logo/alternate/nm_logo_reverse.svg @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="50mm" + height="50mm" + viewBox="0 0 50 50" + version="1.1" + id="svg18" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_logo_reverse.svg" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96"> + <defs + id="defs12" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.979899" + inkscape:cx="199.00923" + inkscape:cy="55.594808" + inkscape:document-units="mm" + inkscape:current-layer="g13" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1" /> + <metadata + id="metadata15"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-53.113205,-106.86279)"> + <g + id="g13"> + <rect + y="106.86279" + x="53.113205" + height="50" + width="50" + id="rect22" + style="fill:#32557d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.37634408;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <g + id="logo" + transform="matrix(0.01061199,0,0,0.01061199,6.8914335,103.86255)" + style="stroke-width:0.58136749" + inkscape:label="logo"> + <path + sodipodi:nodetypes="ccccccccccccccccccccccccc" + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.77839899" + d="M 4921.0159,848.11845 V 4427.6574 h 698.5017 V 2014.3905 l 513.5931,516.0984 -242.4055,243.5876 678.4845,681.8654 242.75,-244.6281 1210.4382,1216.3436 1.3107,1.3175 1.3117,-1.3175 h 476.8721 V 848.12395 H 8023.6883 L 6935.0748,1942.0493 6826.9864,1833.2256 6612.6048,2048.6539 5429.8454,848.11845 Z M 7824.6995,2011.8244 v 1253.4504 l -533.539,-536.1426 213.0014,-214.0412 -90.0745,-90.7209 z" + id="path1482" + inkscape:connector-curvature="0" + inkscape:label="cable" /> + <path + sodipodi:nodetypes="ccccccccccccccccccccc" + style="fill:#32557d;fill-opacity:1;stroke:none;stroke-width:0.77839899" + d="m 6828.2981,2003.7092 -765.3151,769.0494 88.9985,91.2718 203.6838,-204.6779 34.7174,36.2748 36.0988,34.8874 -203.683,204.6775 63.4313,63.7406 202.9925,-203.9839 34.7884,36.2051 36.0279,34.8878 -193.8409,211.4097 57.2489,57.5297 203.0644,-203.984 34.7166,36.2053 36.0304,34.9569 -202.9934,203.984 77.6182,77.997 765.3149,-769.0494 -255.1047,-255.0319 z" + id="path1498" + inkscape:connector-curvature="0" + inkscape:label="connector" /> + </g> + </g> + </g> +</svg> diff --git a/contrib/art/logo/alternate/nm_logotype_vertical.svg b/contrib/art/logo/alternate/nm_logotype_vertical.svg new file mode 100644 index 00000000..8f794128 --- /dev/null +++ b/contrib/art/logo/alternate/nm_logotype_vertical.svg @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="75mm" + height="85mm" + viewBox="0 0 75 85" + version="1.1" + id="svg71" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_logotype_vertical.svg"> + <defs + id="defs65" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1" + inkscape:cx="142.45306" + inkscape:cy="192.65794" + inkscape:document-units="mm" + inkscape:current-layer="g1627" + showgrid="false" + fit-margin-top="0" + fit-margin-right="0" + fit-margin-bottom="0" + fit-margin-left="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1" /> + <metadata + id="metadata68"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-46.225296,-129.40381)"> + <g + id="g1627" + transform="matrix(0.30239406,0,0,0.30239406,-107.25959,-16.360685)" + style="stroke-width:0.87496203"> + <g + id="g1538" + transform="matrix(0.03779528,0,0,0.03779528,358.79361,490.17043)" + style="stroke-width:0.87496203" + inkscape:label="logo"> + <path + inkscape:connector-curvature="0" + id="path1534" + d="M 5029.9666,-215.25215 V 4157.9482 h 853.3745 V 1209.6083 l 627.4666,630.5285 -296.1503,297.5952 828.9187,833.0503 296.5727,-298.8683 1478.8184,1486.0342 1.6018,1.6095 1.6016,-1.6095 h 582.6059 V -215.24452 H 8820.5686 L 7490.5865,1121.2273 7358.5311,988.27476 7096.6167,1251.4682 5651.6129,-215.25215 Z M 8577.4587,1206.4725 v 1531.3676 l -651.8359,-655.0173 260.2277,-261.4989 -110.0444,-110.8354 z" + style="fill:#32557d;fill-opacity:1;stroke:none;stroke-width:1.43124211" + sodipodi:nodetypes="ccccccccccccccccccccccccc" /> + <path + inkscape:connector-curvature="0" + id="path1536" + d="m 7360.1336,1196.5577 -935.0009,939.5654 108.7309,111.5086 248.8436,-250.0604 42.4166,44.3192 44.1019,42.6221 -248.8434,250.0585 77.4949,77.8737 248.0001,-249.2123 42.5016,44.2339 44.0169,42.6214 -236.8198,258.2846 69.9416,70.285 248.0871,-249.2115 42.4169,44.2339 44.0168,42.7063 -248.0007,249.2114 94.8286,95.2918 935,-939.5654 -311.6662,-311.577 z" + style="fill:#ffffff;stroke:none;stroke-width:1.43124211" + sodipodi:nodetypes="ccccccccccccccccccccc" /> + </g> + <g + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726;image-rendering:auto" + transform="matrix(1.3214654,0,0,1.3214654,323.73031,623.26439)" + id="g1548" + inkscape:label="g1548"> + <g + aria-label="Network" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33208466px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726" + id="text1542"> + <path + d="M 160.11453,33.334168 V 51.849521 L 144.95372,33.334168 h -5.83778 v 30.495876 h 6.97049 V 45.314691 l 15.20437,18.515353 h 5.79421 V 33.334168 Z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + id="path16" + inkscape:connector-curvature="0" /> + <path + d="m 177.2528,58.558614 v -4.922906 h 11.41417 V 48.625672 H 177.2528 V 44.05129 h 12.8954 v -5.27143 h -19.69162 v 25.050184 h 20.17084 v -5.27143 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + id="path18" + inkscape:connector-curvature="0" /> + <path + d="m 196.72319,63.830044 h 6.88336 V 44.138421 h 7.88536 V 38.77986 h -22.65408 v 5.358561 h 7.88536 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + id="path20" + inkscape:connector-curvature="0" /> + <path + d="m 243.35874,38.77986 -5.48925,16.685601 -5.315,-16.685601 h -6.36057 l -5.48926,16.380642 -5.27143,-16.380642 h -7.10118 l 8.32102,25.050184 h 7.36257 l 5.22787,-15.857855 5.09717,15.857855 h 7.36257 L 249.9807,38.77986 Z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + id="path22" + inkscape:connector-curvature="0" /> + <path + d="m 261.84822,64.309265 c 8.10319,0 14.07167,-5.489258 14.07167,-12.98253 0,-7.536838 -5.96848,-13.026096 -14.07167,-13.026096 -8.10319,0 -14.07166,5.489258 -14.07166,13.026096 0,7.493272 5.96847,12.98253 14.07166,12.98253 z m 0,-5.619955 c -4.09516,0 -7.14474,-3.093153 -7.14474,-7.362575 0,-4.312988 3.04958,-7.406141 7.14474,-7.406141 4.09516,0 7.10119,3.093153 7.10119,7.406141 0,4.269422 -3.00603,7.362575 -7.10119,7.362575 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + id="path24" + inkscape:connector-curvature="0" /> + <path + d="m 292.97988,63.830044 h 7.40615 l -5.57639,-7.885362 c 3.22385,-1.481229 5.09717,-4.225857 5.09717,-7.928928 0,-5.794216 -4.40012,-9.235894 -11.28348,-9.235894 h -11.41417 v 25.050184 h 6.88336 v -6.709093 h 4.22585 z m 0,-15.81429 c 0,2.43967 -1.61192,3.877333 -4.83577,3.877333 h -4.05159 v -7.754666 h 4.05159 c 3.22385,0 4.83577,1.394097 4.83577,3.877333 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + id="path26" + inkscape:connector-curvature="0" /> + <path + d="m 318.78498,63.830044 h 8.01605 L 315.82252,49.932638 326.23468,38.77986 h -7.5804 L 308.45994,49.453417 V 38.77986 h -6.79622 v 25.050184 h 6.79622 v -6.317003 l 2.83176,-2.918891 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + id="path28" + inkscape:connector-curvature="0" /> + </g> + <g + aria-label="Manager" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33208466px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726" + id="text1546"> + <path + d="m 172.951,105.36586 -0.0858,-30.019376 h -5.70368 L 156.09727,94.001382 144.86144,75.346484 h -5.74656 v 30.019376 h 6.51849 V 87.740198 l 8.79139,14.452182 h 3.13059 l 8.83427,-14.838146 0.0429,18.011626 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + id="path31" + inkscape:connector-curvature="0" /> + <path + d="m 195.84872,105.36586 h 6.69003 L 191.43158,80.707087 h -6.69003 l -11.06428,24.658773 h 6.56137 l 2.05847,-4.88887 h 11.53602 z m -11.49313,-9.77774 3.73098,-8.920043 3.68809,8.920043 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + id="path33" + inkscape:connector-curvature="0" /> + <path + d="M 219.91371,80.707087 V 94.344461 L 208.54923,80.707087 h -5.57503 v 24.658773 h 6.64715 V 91.771371 l 11.36448,13.594489 h 5.57503 V 80.707087 Z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + id="path35" + inkscape:connector-curvature="0" /> + <path + d="m 249.16039,105.36586 h 6.69003 L 244.74325,80.707087 h -6.69003 l -11.06428,24.658773 h 6.56138 l 2.05847,-4.88887 h 11.53601 z m -11.49313,-9.77774 3.73098,-8.920043 3.68809,8.920043 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + id="path37" + inkscape:connector-curvature="0" /> + <path + d="m 272.07754,99.533524 c -1.20077,0.514616 -2.44443,0.771926 -3.85963,0.771926 -4.37425,0 -7.33331,-2.959052 -7.33331,-7.247534 0,-4.374252 3.04483,-7.29042 7.24754,-7.29042 2.44443,0 4.54579,0.900581 6.2183,2.873283 l 4.46002,-3.988288 c -2.44443,-2.959053 -5.96099,-4.417137 -10.76409,-4.417137 -8.10523,0 -13.98045,5.403487 -13.98045,12.822562 0,7.376194 5.74656,12.779674 14.02333,12.779674 3.47368,0 7.41908,-1.115 10.16371,-3.13059 v -9.992163 h -6.17542 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + id="path39" + inkscape:connector-curvature="0" /> + <path + d="M 288.33347,100.1768 V 95.330811 H 299.5693 V 90.399057 H 288.33347 V 85.89615 h 12.69391 v -5.189063 h -19.38394 v 24.658773 h 19.85567 v -5.18906 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + id="path41" + inkscape:connector-curvature="0" /> + <path + d="m 319.50997,105.36586 h 7.29042 l -5.48926,-7.762153 c 3.17348,-1.458084 5.01753,-4.159828 5.01753,-7.805038 0,-5.703681 -4.33137,-9.091582 -11.10717,-9.091582 h -11.23583 v 24.658773 h 6.77581 v -6.604263 h 4.15982 z m 0,-15.567191 c 0,2.40155 -1.58674,3.81675 -4.76022,3.81675 h -3.98828 V 85.98192 h 3.98828 c 3.17348,0 4.76022,1.372314 4.76022,3.816749 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + id="path43" + inkscape:connector-curvature="0" /> + </g> + </g> + </g> + </g> +</svg> diff --git a/contrib/art/logo/nm_logo.svg b/contrib/art/logo/nm_logo.svg new file mode 100644 index 00000000..ce5797dd --- /dev/null +++ b/contrib/art/logo/nm_logo.svg @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="50mm" + height="50mm" + viewBox="0 0 49.999999 49.999999" + version="1.1" + id="svg3790" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_logo.svg"> + <defs + id="defs3784" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.8" + inkscape:cx="89.730775" + inkscape:cy="89.762394" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1" /> + <metadata + id="metadata3787"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="layer01" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-87.115936,-83.441986)"> + <g + id="logo" + transform="matrix(0.01396314,0,0,0.01396314,18.403102,71.599589)" + style="stroke-width:0.58136749" + inkscape:label="logo"> + <path + sodipodi:nodetypes="ccccccccccccccccccccccccc" + style="fill:#32557d;fill-opacity:1;stroke:none;stroke-width:0.77839899" + d="M 4921.0159,848.11845 V 4427.6574 h 698.5017 V 2014.3905 l 513.5931,516.0984 -242.4055,243.5876 678.4845,681.8654 242.75,-244.6281 1210.4382,1216.3436 1.3107,1.3175 1.3117,-1.3175 h 476.8721 V 848.12395 H 8023.6883 L 6935.0748,1942.0493 6826.9864,1833.2256 6612.6048,2048.6539 5429.8454,848.11845 Z M 7824.6995,2011.8244 v 1253.4504 l -533.539,-536.1426 213.0014,-214.0412 -90.0745,-90.7209 z" + id="path1482" + inkscape:connector-curvature="0" + inkscape:label="cable" /> + <path + sodipodi:nodetypes="ccccccccccccccccccccc" + style="fill:#ffffff;stroke:none;stroke-width:0.77839899" + d="m 6828.2981,2003.7092 -765.3151,769.0494 88.9985,91.2718 203.6838,-204.6779 34.7174,36.2748 36.0988,34.8874 -203.683,204.6775 63.4313,63.7406 202.9925,-203.9839 34.7884,36.2051 36.0279,34.8878 -193.8409,211.4097 57.2489,57.5297 203.0644,-203.984 34.7166,36.2053 36.0304,34.9569 -202.9934,203.984 77.6182,77.997 765.3149,-769.0494 -255.1047,-255.0319 z" + id="path1498" + inkscape:connector-curvature="0" + inkscape:label="connector" /> + </g> + </g> +</svg> diff --git a/contrib/art/logo/nm_logotype.svg b/contrib/art/logo/nm_logotype.svg new file mode 100644 index 00000000..d9b57db8 --- /dev/null +++ b/contrib/art/logo/nm_logotype.svg @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="160mm" + height="50mm" + viewBox="0 0 160.00002 50" + version="1.1" + id="svg139" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_logotype.svg"> + <defs + id="defs133" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.98994949" + inkscape:cx="304.81511" + inkscape:cy="-94.216708" + inkscape:document-units="mm" + inkscape:current-layer="text1510" + showgrid="true" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1"> + <inkscape:grid + type="xygrid" + id="grid48" + originx="-14.623603" + originy="-3.9577414" /> + </sodipodi:namedview> + <metadata + id="metadata136"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-18.827967,-33.154443)"> + <g + id="g898" + transform="matrix(1.3356542,0,0,1.3421721,-6.3197222,-28.453313)"> + <path + inkscape:connector-curvature="0" + id="path1482" + d="m 18.827994,45.901532 v 37.239326 h 7.302244 V 58.034709 l 5.369169,5.369172 -2.53414,2.534137 7.09299,7.09371 2.537746,-2.54496 12.654094,12.65409 0.01372,0.01372 0.01372,-0.01372 h 4.985296 V 45.901588 H 51.263822 L 39.883291,57.282115 38.753319,56.149981 36.512137,58.391167 24.147391,45.901532 Z m 30.35554,12.106481 v 13.04013 l -5.577702,-5.577699 2.226749,-2.226753 -0.941643,-0.943805 z" + style="fill:#32557d;fill-opacity:1;stroke:none;stroke-width:0.00811772" + sodipodi:nodetypes="ccccccccccccccccccccccccc" /> + <path + inkscape:connector-curvature="0" + id="path1498" + d="m 38.767002,57.923589 -8.000708,8.000719 0.9304,0.949536 2.129339,-2.129343 0.362945,0.377378 0.377382,0.362949 -2.129339,2.129339 0.663121,0.663118 2.122121,-2.122124 0.363668,0.376654 0.376659,0.362953 -2.026453,2.199375 0.59849,0.598505 2.122859,-2.122124 0.362945,0.376658 0.376655,0.363669 -2.122117,2.122124 0.811435,0.811435 8.000711,-8.000719 -2.666904,-2.653194 z" + style="fill:#ffffff;stroke:none;stroke-width:0.00811772" + sodipodi:nodetypes="ccccccccccccccccccccc" /> + </g> + <g + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.67844689;image-rendering:auto" + transform="matrix(0.52342598,0,0,0.52342598,7.5660564,14.086132)" + id="g1512"> + <g + aria-label="Network" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.67844689" + id="text1506"> + <path + d="M 159.6069,48.258049 V 66.391555 L 144.75875,48.258049 h -5.71738 V 78.125 h 6.82673 V 59.991494 L 160.75891,78.125 h 5.67472 V 48.258049 Z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.6670723px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62945271;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path19" + inkscape:connector-curvature="0" /> + <path + d="m 176.3123,72.962284 v -4.821379 h 11.17878 V 63.234192 H 176.3123 v -4.480043 h 12.62946 V 53.591433 H 169.65624 V 78.125 h 19.75486 v -5.162716 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.6670723px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62945271;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path21" + inkscape:connector-curvature="0" /> + <path + d="m 195.30173,78.125 h 6.7414 V 58.839483 h 7.72274 v -5.24805 h -22.18688 v 5.24805 h 7.72274 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.6670723px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62945271;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path23" + inkscape:connector-curvature="0" /> + <path + d="m 240.89608,53.591433 -5.37605,16.341489 -5.20538,-16.341489 h -6.22939 l -5.37606,16.04282 -5.16271,-16.04282 h -6.95473 L 214.74117,78.125 h 7.21073 L 227.07195,62.594186 232.064,78.125 h 7.21073 l 8.10675,-24.533567 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.6670723px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62945271;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path25" + inkscape:connector-curvature="0" /> + <path + d="m 258.92484,78.594338 c 7.93607,0 13.78146,-5.376051 13.78146,-12.714788 0,-7.381403 -5.84539,-12.757454 -13.78146,-12.757454 -7.93608,0 -13.78147,5.376051 -13.78147,12.757454 0,7.338737 5.84539,12.714788 13.78147,12.714788 z m 0,-5.504053 c -4.01071,0 -6.9974,-3.029362 -6.9974,-7.210735 0,-4.22404 2.98669,-7.253402 6.9974,-7.253402 4.0107,0 6.95473,3.029362 6.95473,7.253402 0,4.181373 -2.94403,7.210735 -6.95473,7.210735 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.6670723px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62945271;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path27" + inkscape:connector-curvature="0" /> + <path + d="m 289.33503,78.125 h 7.2534 l -5.46138,-7.72274 c 3.15736,-1.450681 4.99205,-4.138706 4.99205,-7.765407 0,-5.674721 -4.30938,-9.04542 -11.05078,-9.04542 H 273.88955 V 78.125 h 6.7414 v -6.570729 h 4.13871 z m 0,-15.488147 c 0,2.389356 -1.57868,3.797369 -4.73604,3.797369 h -3.96804 v -7.594739 h 3.96804 c 3.15736,0 4.73604,1.365347 4.73604,3.79737 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.6670723px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62945271;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path29" + inkscape:connector-curvature="0" /> + <path + d="m 314.52853,78.125 h 7.85074 L 311.62717,64.514204 321.8246,53.591433 h -7.42407 l -9.9841,10.453433 V 53.591433 h -6.65606 V 78.125 h 6.65606 v -6.186725 l 2.77336,-2.858694 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.6670723px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62945271;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path31" + inkscape:connector-curvature="0" /> + </g> + <g + aria-label="Manager" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.67844689" + id="text1510"> + <path + d="m 172.46926,119.66083 -0.0847,-29.632662 h -5.63021 L 155.83264,108.44275 144.74156,90.028168 h -5.67254 v 29.632662 h 6.43452 v -17.39861 l 8.67814,14.26601 h 3.09026 l 8.72047,-14.647 0.0423,17.7796 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.76127887px;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path34" + inkscape:connector-curvature="0" /> + <path + d="m 194.72972,119.66083 h 6.60385 L 190.36949,95.319714 h -6.60385 l -10.92175,24.341116 h 6.47685 l 2.03195,-4.82589 h 11.38741 z m -11.34507,-9.65178 3.68291,-8.80514 3.64059,8.80514 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.76127887px;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path36" + inkscape:connector-curvature="0" /> + <path + d="M 218.14242,95.319714 V 108.78141 L 206.92434,95.319714 h -5.50321 v 24.341116 h 6.56152 v -13.41936 l 11.21808,13.41936 h 5.5032 V 95.319714 Z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.76127887px;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path38" + inkscape:connector-curvature="0" /> + <path + d="m 246.67005,119.66083 h 6.60385 L 242.30982,95.319714 h -6.60385 l -10.92175,24.341116 h 6.47685 l 2.03195,-4.82589 h 11.38741 z m -11.34507,-9.65178 3.68291,-8.80514 3.64059,8.80514 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.76127887px;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path40" + inkscape:connector-curvature="0" /> + <path + d="m 268.94969,113.90363 c -1.18531,0.50798 -2.41295,0.76198 -3.80991,0.76198 -4.31791,0 -7.23884,-2.92094 -7.23884,-7.15417 0,-4.3179 3.0056,-7.19651 7.15417,-7.19651 2.41295,0 4.48723,0.88898 6.1382,2.83627 l 4.40256,-3.936907 c -2.41294,-2.920934 -5.8842,-4.360235 -10.62542,-4.360235 -8.00082,0 -13.80036,5.333882 -13.80036,12.657382 0,7.28116 5.67254,12.61504 13.84269,12.61504 3.42892,0 7.3235,-1.10064 10.03277,-3.09026 v -9.86344 h -6.09586 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.76127887px;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path42" + inkscape:connector-curvature="0" /> + <path + d="m 284.65392,114.53861 v -4.78356 h 11.09109 v -4.86822 h -11.09109 v -4.4449 h 12.53039 v -5.122216 h -19.13424 v 24.341116 h 19.59989 v -5.12222 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.76127887px;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path44" + inkscape:connector-curvature="0" /> + <path + d="m 315.08652,119.66083 h 7.1965 l -5.41854,-7.66216 c 3.1326,-1.4393 4.95289,-4.10624 4.95289,-7.70449 0,-5.630208 -4.27557,-8.974466 -10.96409,-8.974466 H 299.7622 v 24.341116 h 6.68852 v -6.51919 h 4.10624 z m 0,-15.36665 c 0,2.37061 -1.5663,3.76758 -4.69889,3.76758 h -3.93691 v -7.53516 h 3.93691 c 3.13259,0 4.69889,1.35463 4.69889,3.76758 z" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33237076px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.76127887px;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke-width:0.67844689" + id="path46" + inkscape:connector-curvature="0" /> + </g> + </g> + </g> +</svg> diff --git a/contrib/art/logo/stickers/nm_sticker_blue.svg b/contrib/art/logo/stickers/nm_sticker_blue.svg new file mode 100644 index 00000000..a46e22ea --- /dev/null +++ b/contrib/art/logo/stickers/nm_sticker_blue.svg @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="47.889584mm" + height="54.9034mm" + viewBox="0 0 47.889584 54.9034" + version="1.1" + id="svg875" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_sticker02.svg"> + <defs + id="defs869" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.8" + inkscape:cx="51.83594" + inkscape:cy="64.38986" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1" /> + <metadata + id="metadata872"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-81.055209,-121.0483)"> + <polygon + transform="matrix(0.26458333,0,0,0.26458333,81.055209,120.85104)" + id="Polygon-1" + points="181,156.37723 90.5,208.25446 -1.5258905e-12,156.37723 -1.5827339e-12,52.622771 90.5,0.74554102 181,52.622771 " + style="fill:#24446a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1" /> + <polygon + transform="matrix(0.26458333,0,0,0.26458333,81.055209,120.85104)" + id="Polygon-1-Copy" + points="169.18938,59.435072 169.18938,149.56493 90.5,194.62986 11.810617,149.56493 11.810617,59.435072 90.5,14.370145 " + style="fill:#32557d;fill-rule:evenodd;stroke:none;stroke-width:1;fill-opacity:1" + inkscape:label="Polygon-2" /> + <g + id="g981" + transform="matrix(1.0214286,0,0,1.0214286,-2.3225275,-2.3090111)"> + <g + inkscape:label="logo" + style="stroke-width:0.87496203" + transform="matrix(0.00426687,0,0,0.00426687,74.275548,131.38514)" + id="g1538"> + <path + sodipodi:nodetypes="ccccccccccccccccccccccccc" + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.43124211" + d="M 5029.9666,-215.25215 V 4157.9482 h 853.3745 V 1209.6083 l 627.4666,630.5285 -296.1503,297.5952 828.9187,833.0503 296.5727,-298.8683 1478.8184,1486.0342 1.6018,1.6095 1.6016,-1.6095 h 582.6059 V -215.24452 H 8820.5686 L 7490.5865,1121.2273 7358.5311,988.27476 7096.6167,1251.4682 5651.6129,-215.25215 Z M 8577.4587,1206.4725 v 1531.3676 l -651.8359,-655.0173 260.2277,-261.4989 -110.0444,-110.8354 z" + id="path1534" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccccccccccccccccccccc" + style="fill:#32557d;fill-opacity:1;stroke:none;stroke-width:1.43124211" + d="m 7360.1336,1196.5577 -935.0009,939.5654 108.7309,111.5086 248.8436,-250.0604 42.4166,44.3192 44.1019,42.6221 -248.8434,250.0585 77.4949,77.8737 248.0001,-249.2123 42.5016,44.2339 44.0169,42.6214 -236.8198,258.2846 69.9416,70.285 248.0871,-249.2115 42.4169,44.2339 44.0168,42.7063 -248.0007,249.2114 94.8286,95.2918 935,-939.5654 -311.6662,-311.577 z" + id="path1536" + inkscape:connector-curvature="0" /> + </g> + <g + inkscape:label="g1548" + id="g1548" + transform="matrix(0.14918522,0,0,0.14918522,70.31712,146.41059)" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726;image-rendering:auto"> + <g + id="text1542" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33208466px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726" + aria-label="Network"> + <path + inkscape:connector-curvature="0" + id="path16" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="M 160.11453,33.334168 V 51.849521 L 144.95372,33.334168 h -5.83778 v 30.495876 h 6.97049 V 45.314691 l 15.20437,18.515353 h 5.79421 V 33.334168 Z" /> + <path + inkscape:connector-curvature="0" + id="path18" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 177.2528,58.558614 v -4.922906 h 11.41417 V 48.625672 H 177.2528 V 44.05129 h 12.8954 v -5.27143 h -19.69162 v 25.050184 h 20.17084 v -5.27143 z" /> + <path + inkscape:connector-curvature="0" + id="path20" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 196.72319,63.830044 h 6.88336 V 44.138421 h 7.88536 V 38.77986 h -22.65408 v 5.358561 h 7.88536 z" /> + <path + inkscape:connector-curvature="0" + id="path22" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 243.35874,38.77986 -5.48925,16.685601 -5.315,-16.685601 h -6.36057 l -5.48926,16.380642 -5.27143,-16.380642 h -7.10118 l 8.32102,25.050184 h 7.36257 l 5.22787,-15.857855 5.09717,15.857855 h 7.36257 L 249.9807,38.77986 Z" /> + <path + inkscape:connector-curvature="0" + id="path24" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 261.84822,64.309265 c 8.10319,0 14.07167,-5.489258 14.07167,-12.98253 0,-7.536838 -5.96848,-13.026096 -14.07167,-13.026096 -8.10319,0 -14.07166,5.489258 -14.07166,13.026096 0,7.493272 5.96847,12.98253 14.07166,12.98253 z m 0,-5.619955 c -4.09516,0 -7.14474,-3.093153 -7.14474,-7.362575 0,-4.312988 3.04958,-7.406141 7.14474,-7.406141 4.09516,0 7.10119,3.093153 7.10119,7.406141 0,4.269422 -3.00603,7.362575 -7.10119,7.362575 z" /> + <path + inkscape:connector-curvature="0" + id="path26" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 292.97988,63.830044 h 7.40615 l -5.57639,-7.885362 c 3.22385,-1.481229 5.09717,-4.225857 5.09717,-7.928928 0,-5.794216 -4.40012,-9.235894 -11.28348,-9.235894 h -11.41417 v 25.050184 h 6.88336 v -6.709093 h 4.22585 z m 0,-15.81429 c 0,2.43967 -1.61192,3.877333 -4.83577,3.877333 h -4.05159 v -7.754666 h 4.05159 c 3.22385,0 4.83577,1.394097 4.83577,3.877333 z" /> + <path + inkscape:connector-curvature="0" + id="path28" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 318.78498,63.830044 h 8.01605 L 315.82252,49.932638 326.23468,38.77986 h -7.5804 L 308.45994,49.453417 V 38.77986 h -6.79622 v 25.050184 h 6.79622 v -6.317003 l 2.83176,-2.918891 z" /> + </g> + <g + id="text1546" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33208466px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726" + aria-label="Manager"> + <path + inkscape:connector-curvature="0" + id="path31" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 172.951,105.36586 -0.0858,-30.019376 h -5.70368 L 156.09727,94.001382 144.86144,75.346484 h -5.74656 v 30.019376 h 6.51849 V 87.740198 l 8.79139,14.452182 h 3.13059 l 8.83427,-14.838146 0.0429,18.011626 z" /> + <path + inkscape:connector-curvature="0" + id="path33" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 195.84872,105.36586 h 6.69003 L 191.43158,80.707087 h -6.69003 l -11.06428,24.658773 h 6.56137 l 2.05847,-4.88887 h 11.53602 z m -11.49313,-9.77774 3.73098,-8.920043 3.68809,8.920043 z" /> + <path + inkscape:connector-curvature="0" + id="path35" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="M 219.91371,80.707087 V 94.344461 L 208.54923,80.707087 h -5.57503 v 24.658773 h 6.64715 V 91.771371 l 11.36448,13.594489 h 5.57503 V 80.707087 Z" /> + <path + inkscape:connector-curvature="0" + id="path37" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 249.16039,105.36586 h 6.69003 L 244.74325,80.707087 h -6.69003 l -11.06428,24.658773 h 6.56138 l 2.05847,-4.88887 h 11.53601 z m -11.49313,-9.77774 3.73098,-8.920043 3.68809,8.920043 z" /> + <path + inkscape:connector-curvature="0" + id="path39" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 272.07754,99.533524 c -1.20077,0.514616 -2.44443,0.771926 -3.85963,0.771926 -4.37425,0 -7.33331,-2.959052 -7.33331,-7.247534 0,-4.374252 3.04483,-7.29042 7.24754,-7.29042 2.44443,0 4.54579,0.900581 6.2183,2.873283 l 4.46002,-3.988288 c -2.44443,-2.959053 -5.96099,-4.417137 -10.76409,-4.417137 -8.10523,0 -13.98045,5.403487 -13.98045,12.822562 0,7.376194 5.74656,12.779674 14.02333,12.779674 3.47368,0 7.41908,-1.115 10.16371,-3.13059 v -9.992163 h -6.17542 z" /> + <path + inkscape:connector-curvature="0" + id="path41" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="M 288.33347,100.1768 V 95.330811 H 299.5693 V 90.399057 H 288.33347 V 85.89615 h 12.69391 v -5.189063 h -19.38394 v 24.658773 h 19.85567 v -5.18906 z" /> + <path + inkscape:connector-curvature="0" + id="path43" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 319.50997,105.36586 h 7.29042 l -5.48926,-7.762153 c 3.17348,-1.458084 5.01753,-4.159828 5.01753,-7.805038 0,-5.703681 -4.33137,-9.091582 -11.10717,-9.091582 h -11.23583 v 24.658773 h 6.77581 v -6.604263 h 4.15982 z m 0,-15.567191 c 0,2.40155 -1.58674,3.81675 -4.76022,3.81675 h -3.98828 V 85.98192 h 3.98828 c 3.17348,0 4.76022,1.372314 4.76022,3.816749 z" /> + </g> + </g> + </g> + </g> +</svg> diff --git a/contrib/art/logo/stickers/nm_sticker_red.svg b/contrib/art/logo/stickers/nm_sticker_red.svg new file mode 100644 index 00000000..702f3c76 --- /dev/null +++ b/contrib/art/logo/stickers/nm_sticker_red.svg @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="47.889584mm" + height="54.9034mm" + viewBox="0 0 47.889584 54.9034" + version="1.1" + id="svg875" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_sticker03.svg"> + <defs + id="defs869" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.98994949" + inkscape:cx="-343.27723" + inkscape:cy="-18.266293" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1" /> + <metadata + id="metadata872"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-81.055209,-121.0483)"> + <polygon + transform="matrix(0.26458333,0,0,0.26458333,81.055209,120.85104)" + id="Polygon-1" + points="-1.5827339e-12,52.622771 90.5,0.74554102 181,52.622771 181,156.37723 90.5,208.25446 -1.5258905e-12,156.37723 " + style="fill:#bb0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1" /> + <polygon + transform="matrix(0.26458333,0,0,0.26458333,81.055209,120.85104)" + id="Polygon-1-Copy" + points="11.810617,149.56493 11.810617,59.435072 90.5,14.370145 169.18938,59.435072 169.18938,149.56493 90.5,194.62986 " + style="fill:#cc0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1" + inkscape:label="Polygon-2" /> + <g + id="g981" + transform="matrix(1.0214286,0,0,1.0214286,-2.3225275,-2.3090111)"> + <g + inkscape:label="logo" + style="stroke-width:0.87496203" + transform="matrix(0.00426687,0,0,0.00426687,74.275548,131.38514)" + id="g1538"> + <path + sodipodi:nodetypes="ccccccccccccccccccccccccc" + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.43124211" + d="M 5029.9666,-215.25215 V 4157.9482 h 853.3745 V 1209.6083 l 627.4666,630.5285 -296.1503,297.5952 828.9187,833.0503 296.5727,-298.8683 1478.8184,1486.0342 1.6018,1.6095 1.6016,-1.6095 h 582.6059 V -215.24452 H 8820.5686 L 7490.5865,1121.2273 7358.5311,988.27476 7096.6167,1251.4682 5651.6129,-215.25215 Z M 8577.4587,1206.4725 v 1531.3676 l -651.8359,-655.0173 260.2277,-261.4989 -110.0444,-110.8354 z" + id="path1534" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccccccccccccccccccccc" + style="fill:#cc0000;fill-opacity:1;stroke:none;stroke-width:1.43124211" + d="m 7360.1336,1196.5577 -935.0009,939.5654 108.7309,111.5086 248.8436,-250.0604 42.4166,44.3192 44.1019,42.6221 -248.8434,250.0585 77.4949,77.8737 248.0001,-249.2123 42.5016,44.2339 44.0169,42.6214 -236.8198,258.2846 69.9416,70.285 248.0871,-249.2115 42.4169,44.2339 44.0168,42.7063 -248.0007,249.2114 94.8286,95.2918 935,-939.5654 -311.6662,-311.577 z" + id="path1536" + inkscape:connector-curvature="0" /> + </g> + <g + inkscape:label="g1548" + id="g1548" + transform="matrix(0.14918522,0,0,0.14918522,70.31712,146.41059)" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726;image-rendering:auto"> + <g + id="text1542" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33208466px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726" + aria-label="Network"> + <path + inkscape:connector-curvature="0" + id="path16" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="M 160.11453,33.334168 V 51.849521 L 144.95372,33.334168 h -5.83778 v 30.495876 h 6.97049 V 45.314691 l 15.20437,18.515353 h 5.79421 V 33.334168 Z" /> + <path + inkscape:connector-curvature="0" + id="path18" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 177.2528,58.558614 v -4.922906 h 11.41417 V 48.625672 H 177.2528 V 44.05129 h 12.8954 v -5.27143 h -19.69162 v 25.050184 h 20.17084 v -5.27143 z" /> + <path + inkscape:connector-curvature="0" + id="path20" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 196.72319,63.830044 h 6.88336 V 44.138421 h 7.88536 V 38.77986 h -22.65408 v 5.358561 h 7.88536 z" /> + <path + inkscape:connector-curvature="0" + id="path22" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 243.35874,38.77986 -5.48925,16.685601 -5.315,-16.685601 h -6.36057 l -5.48926,16.380642 -5.27143,-16.380642 h -7.10118 l 8.32102,25.050184 h 7.36257 l 5.22787,-15.857855 5.09717,15.857855 h 7.36257 L 249.9807,38.77986 Z" /> + <path + inkscape:connector-curvature="0" + id="path24" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 261.84822,64.309265 c 8.10319,0 14.07167,-5.489258 14.07167,-12.98253 0,-7.536838 -5.96848,-13.026096 -14.07167,-13.026096 -8.10319,0 -14.07166,5.489258 -14.07166,13.026096 0,7.493272 5.96847,12.98253 14.07166,12.98253 z m 0,-5.619955 c -4.09516,0 -7.14474,-3.093153 -7.14474,-7.362575 0,-4.312988 3.04958,-7.406141 7.14474,-7.406141 4.09516,0 7.10119,3.093153 7.10119,7.406141 0,4.269422 -3.00603,7.362575 -7.10119,7.362575 z" /> + <path + inkscape:connector-curvature="0" + id="path26" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 292.97988,63.830044 h 7.40615 l -5.57639,-7.885362 c 3.22385,-1.481229 5.09717,-4.225857 5.09717,-7.928928 0,-5.794216 -4.40012,-9.235894 -11.28348,-9.235894 h -11.41417 v 25.050184 h 6.88336 v -6.709093 h 4.22585 z m 0,-15.81429 c 0,2.43967 -1.61192,3.877333 -4.83577,3.877333 h -4.05159 v -7.754666 h 4.05159 c 3.22385,0 4.83577,1.394097 4.83577,3.877333 z" /> + <path + inkscape:connector-curvature="0" + id="path28" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 318.78498,63.830044 h 8.01605 L 315.82252,49.932638 326.23468,38.77986 h -7.5804 L 308.45994,49.453417 V 38.77986 h -6.79622 v 25.050184 h 6.79622 v -6.317003 l 2.83176,-2.918891 z" /> + </g> + <g + id="text1546" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33208466px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726" + aria-label="Manager"> + <path + inkscape:connector-curvature="0" + id="path31" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 172.951,105.36586 -0.0858,-30.019376 h -5.70368 L 156.09727,94.001382 144.86144,75.346484 h -5.74656 v 30.019376 h 6.51849 V 87.740198 l 8.79139,14.452182 h 3.13059 l 8.83427,-14.838146 0.0429,18.011626 z" /> + <path + inkscape:connector-curvature="0" + id="path33" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 195.84872,105.36586 h 6.69003 L 191.43158,80.707087 h -6.69003 l -11.06428,24.658773 h 6.56137 l 2.05847,-4.88887 h 11.53602 z m -11.49313,-9.77774 3.73098,-8.920043 3.68809,8.920043 z" /> + <path + inkscape:connector-curvature="0" + id="path35" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="M 219.91371,80.707087 V 94.344461 L 208.54923,80.707087 h -5.57503 v 24.658773 h 6.64715 V 91.771371 l 11.36448,13.594489 h 5.57503 V 80.707087 Z" /> + <path + inkscape:connector-curvature="0" + id="path37" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 249.16039,105.36586 h 6.69003 L 244.74325,80.707087 h -6.69003 l -11.06428,24.658773 h 6.56138 l 2.05847,-4.88887 h 11.53601 z m -11.49313,-9.77774 3.73098,-8.920043 3.68809,8.920043 z" /> + <path + inkscape:connector-curvature="0" + id="path39" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 272.07754,99.533524 c -1.20077,0.514616 -2.44443,0.771926 -3.85963,0.771926 -4.37425,0 -7.33331,-2.959052 -7.33331,-7.247534 0,-4.374252 3.04483,-7.29042 7.24754,-7.29042 2.44443,0 4.54579,0.900581 6.2183,2.873283 l 4.46002,-3.988288 c -2.44443,-2.959053 -5.96099,-4.417137 -10.76409,-4.417137 -8.10523,0 -13.98045,5.403487 -13.98045,12.822562 0,7.376194 5.74656,12.779674 14.02333,12.779674 3.47368,0 7.41908,-1.115 10.16371,-3.13059 v -9.992163 h -6.17542 z" /> + <path + inkscape:connector-curvature="0" + id="path41" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="M 288.33347,100.1768 V 95.330811 H 299.5693 V 90.399057 H 288.33347 V 85.89615 h 12.69391 v -5.189063 h -19.38394 v 24.658773 h 19.85567 v -5.18906 z" /> + <path + inkscape:connector-curvature="0" + id="path43" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1" + d="m 319.50997,105.36586 h 7.29042 l -5.48926,-7.762153 c 3.17348,-1.458084 5.01753,-4.159828 5.01753,-7.805038 0,-5.703681 -4.33137,-9.091582 -11.10717,-9.091582 h -11.23583 v 24.658773 h 6.77581 v -6.604263 h 4.15982 z m 0,-15.567191 c 0,2.40155 -1.58674,3.81675 -4.76022,3.81675 h -3.98828 V 85.98192 h 3.98828 c 3.17348,0 4.76022,1.372314 4.76022,3.816749 z" /> + </g> + </g> + </g> + </g> +</svg> diff --git a/contrib/art/logo/stickers/nm_sticker_white.svg b/contrib/art/logo/stickers/nm_sticker_white.svg new file mode 100644 index 00000000..5a29e431 --- /dev/null +++ b/contrib/art/logo/stickers/nm_sticker_white.svg @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="47.889584mm" + height="54.9034mm" + viewBox="0 0 47.889584 54.9034" + version="1.1" + id="svg875" + inkscape:version="0.92.4 (unknown)" + sodipodi:docname="nm_sticker01.svg"> + <defs + id="defs869" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.98994949" + inkscape:cx="-145.1366" + inkscape:cy="-4.9536298" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1280" + inkscape:window-y="27" + inkscape:window-maximized="1" /> + <metadata + id="metadata872"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-81.055209,-121.0483)"> + <polygon + transform="matrix(0.26458333,0,0,0.26458333,81.055209,120.85104)" + id="Polygon-1" + points="-1.5258905e-12,156.37723 -1.5827339e-12,52.622771 90.5,0.74554102 181,52.622771 181,156.37723 90.5,208.25446 " + style="fill:#32557d;fill-rule:evenodd;stroke:none;stroke-width:1;fill-opacity:1" /> + <polygon + transform="matrix(0.26458333,0,0,0.26458333,81.055209,120.85104)" + id="Polygon-1-Copy" + points="90.5,194.62986 11.810617,149.56493 11.810617,59.435072 90.5,14.370145 169.18938,59.435072 169.18938,149.56493 " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1" + inkscape:label="Polygon-2" /> + <g + id="g981" + transform="matrix(1.0214286,0,0,1.0214286,-2.3225275,-2.3090111)"> + <g + inkscape:label="logo" + style="stroke-width:0.87496203" + transform="matrix(0.00426687,0,0,0.00426687,74.275548,131.38514)" + id="g1538"> + <path + sodipodi:nodetypes="ccccccccccccccccccccccccc" + style="fill:#32557d;fill-opacity:1;stroke:none;stroke-width:1.43124211" + d="M 5029.9666,-215.25215 V 4157.9482 h 853.3745 V 1209.6083 l 627.4666,630.5285 -296.1503,297.5952 828.9187,833.0503 296.5727,-298.8683 1478.8184,1486.0342 1.6018,1.6095 1.6016,-1.6095 h 582.6059 V -215.24452 H 8820.5686 L 7490.5865,1121.2273 7358.5311,988.27476 7096.6167,1251.4682 5651.6129,-215.25215 Z M 8577.4587,1206.4725 v 1531.3676 l -651.8359,-655.0173 260.2277,-261.4989 -110.0444,-110.8354 z" + id="path1534" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccccccccccccccccccccc" + style="fill:#ffffff;stroke:none;stroke-width:1.43124211" + d="m 7360.1336,1196.5577 -935.0009,939.5654 108.7309,111.5086 248.8436,-250.0604 42.4166,44.3192 44.1019,42.6221 -248.8434,250.0585 77.4949,77.8737 248.0001,-249.2123 42.5016,44.2339 44.0169,42.6214 -236.8198,258.2846 69.9416,70.285 248.0871,-249.2115 42.4169,44.2339 44.0168,42.7063 -248.0007,249.2114 94.8286,95.2918 935,-939.5654 -311.6662,-311.577 z" + id="path1536" + inkscape:connector-curvature="0" /> + </g> + <g + inkscape:label="g1548" + id="g1548" + transform="matrix(0.14918522,0,0,0.14918522,70.31712,146.41059)" + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726;image-rendering:auto"> + <g + id="text1542" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33208466px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726" + aria-label="Network"> + <path + inkscape:connector-curvature="0" + id="path16" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + d="M 160.11453,33.334168 V 51.849521 L 144.95372,33.334168 h -5.83778 v 30.495876 h 6.97049 V 45.314691 l 15.20437,18.515353 h 5.79421 V 33.334168 Z" /> + <path + inkscape:connector-curvature="0" + id="path18" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + d="m 177.2528,58.558614 v -4.922906 h 11.41417 V 48.625672 H 177.2528 V 44.05129 h 12.8954 v -5.27143 h -19.69162 v 25.050184 h 20.17084 v -5.27143 z" /> + <path + inkscape:connector-curvature="0" + id="path20" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + d="m 196.72319,63.830044 h 6.88336 V 44.138421 h 7.88536 V 38.77986 h -22.65408 v 5.358561 h 7.88536 z" /> + <path + inkscape:connector-curvature="0" + id="path22" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + d="m 243.35874,38.77986 -5.48925,16.685601 -5.315,-16.685601 h -6.36057 l -5.48926,16.380642 -5.27143,-16.380642 h -7.10118 l 8.32102,25.050184 h 7.36257 l 5.22787,-15.857855 5.09717,15.857855 h 7.36257 L 249.9807,38.77986 Z" /> + <path + inkscape:connector-curvature="0" + id="path24" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + d="m 261.84822,64.309265 c 8.10319,0 14.07167,-5.489258 14.07167,-12.98253 0,-7.536838 -5.96848,-13.026096 -14.07167,-13.026096 -8.10319,0 -14.07166,5.489258 -14.07166,13.026096 0,7.493272 5.96847,12.98253 14.07166,12.98253 z m 0,-5.619955 c -4.09516,0 -7.14474,-3.093153 -7.14474,-7.362575 0,-4.312988 3.04958,-7.406141 7.14474,-7.406141 4.09516,0 7.10119,3.093153 7.10119,7.406141 0,4.269422 -3.00603,7.362575 -7.10119,7.362575 z" /> + <path + inkscape:connector-curvature="0" + id="path26" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + d="m 292.97988,63.830044 h 7.40615 l -5.57639,-7.885362 c 3.22385,-1.481229 5.09717,-4.225857 5.09717,-7.928928 0,-5.794216 -4.40012,-9.235894 -11.28348,-9.235894 h -11.41417 v 25.050184 h 6.88336 v -6.709093 h 4.22585 z m 0,-15.81429 c 0,2.43967 -1.61192,3.877333 -4.83577,3.877333 h -4.05159 v -7.754666 h 4.05159 c 3.22385,0 4.83577,1.394097 4.83577,3.877333 z" /> + <path + inkscape:connector-curvature="0" + id="path28" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:43.5655365px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-3.62478876px;writing-mode:lr-tb;text-anchor:start" + d="m 318.78498,63.830044 h 8.01605 L 315.82252,49.932638 326.23468,38.77986 h -7.5804 L 308.45994,49.453417 V 38.77986 h -6.79622 v 25.050184 h 6.79622 v -6.317003 l 2.83176,-2.918891 z" /> + </g> + <g + id="text1546" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.33208466px;line-height:1.25;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.02106726" + aria-label="Manager"> + <path + inkscape:connector-curvature="0" + id="path31" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + d="m 172.951,105.36586 -0.0858,-30.019376 h -5.70368 L 156.09727,94.001382 144.86144,75.346484 h -5.74656 v 30.019376 h 6.51849 V 87.740198 l 8.79139,14.452182 h 3.13059 l 8.83427,-14.838146 0.0429,18.011626 z" /> + <path + inkscape:connector-curvature="0" + id="path33" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + d="m 195.84872,105.36586 h 6.69003 L 191.43158,80.707087 h -6.69003 l -11.06428,24.658773 h 6.56137 l 2.05847,-4.88887 h 11.53602 z m -11.49313,-9.77774 3.73098,-8.920043 3.68809,8.920043 z" /> + <path + inkscape:connector-curvature="0" + id="path35" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + d="M 219.91371,80.707087 V 94.344461 L 208.54923,80.707087 h -5.57503 v 24.658773 h 6.64715 V 91.771371 l 11.36448,13.594489 h 5.57503 V 80.707087 Z" /> + <path + inkscape:connector-curvature="0" + id="path37" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + d="m 249.16039,105.36586 h 6.69003 L 244.74325,80.707087 h -6.69003 l -11.06428,24.658773 h 6.56138 l 2.05847,-4.88887 h 11.53601 z m -11.49313,-9.77774 3.73098,-8.920043 3.68809,8.920043 z" /> + <path + inkscape:connector-curvature="0" + id="path39" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + d="m 272.07754,99.533524 c -1.20077,0.514616 -2.44443,0.771926 -3.85963,0.771926 -4.37425,0 -7.33331,-2.959052 -7.33331,-7.247534 0,-4.374252 3.04483,-7.29042 7.24754,-7.29042 2.44443,0 4.54579,0.900581 6.2183,2.873283 l 4.46002,-3.988288 c -2.44443,-2.959053 -5.96099,-4.417137 -10.76409,-4.417137 -8.10523,0 -13.98045,5.403487 -13.98045,12.822562 0,7.376194 5.74656,12.779674 14.02333,12.779674 3.47368,0 7.41908,-1.115 10.16371,-3.13059 v -9.992163 h -6.17542 z" /> + <path + inkscape:connector-curvature="0" + id="path41" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + d="M 288.33347,100.1768 V 95.330811 H 299.5693 V 90.399057 H 288.33347 V 85.89615 h 12.69391 v -5.189063 h -19.38394 v 24.658773 h 19.85567 v -5.18906 z" /> + <path + inkscape:connector-curvature="0" + id="path43" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.88482285px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Bold';font-variant-ligatures:normal;font-variant-caps:small-caps;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:-2.45056129px;writing-mode:lr-tb;text-anchor:start" + d="m 319.50997,105.36586 h 7.29042 l -5.48926,-7.762153 c 3.17348,-1.458084 5.01753,-4.159828 5.01753,-7.805038 0,-5.703681 -4.33137,-9.091582 -11.10717,-9.091582 h -11.23583 v 24.658773 h 6.77581 v -6.604263 h 4.15982 z m 0,-15.567191 c 0,2.40155 -1.58674,3.81675 -4.76022,3.81675 h -3.98828 V 85.98192 h 3.98828 c 3.17348,0 4.76022,1.372314 4.76022,3.816749 z" /> + </g> + </g> + </g> + </g> +</svg> diff --git a/contrib/debian/REQUIRED_PACKAGES b/contrib/debian/REQUIRED_PACKAGES new file mode 100755 index 00000000..8e68bba3 --- /dev/null +++ b/contrib/debian/REQUIRED_PACKAGES @@ -0,0 +1,106 @@ +#!/bin/bash + +set -xe + +# A list of packages useful/needed to build and develop +# NetworkManager on Debian and Ubuntu. +# +# Not all of these packages are available, depending +# on your distribution/release. But the script will happily +# skip them. +# +# Not all of these packages are strictly speaking necessary. +# This is a generous list of related packages. + +SUDO= +[ "$EUID" -eq 0 ] || SUDO=sudo + +install() { + if [ "$NM_INSTALL" != "" ]; then + $NM_INSTALL "$@" + else + $SUDO apt-get install -y "$@" + fi +} + +install_ignore_missing() { + for p; do + install "$p" || : + done +} + + +install \ + \ + autoconf \ + automake \ + autopoint \ + clang \ + dbus \ + dbus-x11 \ + dnsmasq \ + git \ + gobject-introspection \ + gtk-doc-tools \ + intltool \ + iproute2 \ + iptables \ + libaudit-dev \ + libcurl4-gnutls-dev \ + libdbus-1-dev \ + libgirepository1.0-dev \ + libglib2.0-dev \ + libglib2.0-doc \ + libgnutls28-dev \ + libiw-dev \ + libjansson-dev \ + libjansson4 \ + libmm-glib-dev \ + libndp-dev \ + libnewt-dev \ + libnss3-dev \ + libpolkit-gobject-1-dev \ + libpsl-dev \ + libreadline-dev \ + libsystemd-dev \ + libteam-dev \ + libtool \ + libudev-dev \ + locales \ + make \ + meson \ + mobile-broadband-provider-info \ + pkg-config \ + policykit-1 \ + ppp \ + ppp-dev \ + python3-dbus \ + python3-gi \ + python3-pip \ + python3-setuptools \ + python3-pexpect \ + udev \ + uuid-dev \ + valgrind \ + \ + #end + +install_ignore_missing \ + python-setuptools \ + \ + #end + +# Old. Present in Ubuntu 20.04, not in Ubuntu 22.04 +# software-properties-common +install_ignore_missing \ + python-dbus \ + python-gi \ + \ + #end + +# Old. Present in Ubuntu 18.04, not in Ubuntu 20.04 +# software-properties-common +install_ignore_missing \ + libgcrypt11-dev \ + \ + #end diff --git a/contrib/editors/networkmanager-style.el b/contrib/editors/networkmanager-style.el new file mode 100644 index 00000000..69c7ac5e --- /dev/null +++ b/contrib/editors/networkmanager-style.el @@ -0,0 +1,61 @@ +;;; Emacs support for hacking on NetworkManager + +(c-add-style "NetworkManager" + '( + ; Start with the "bsd" style + "bsd" + + ; ...but remove the rule saying labels must be indented at + ; least one space + (c-label-minimum-indentation . 0) + + ; 4-space tabs/indents + (tab-width . 4) + (c-basic-offset . 4) + + ; Use smart-tabs-mode (see below) to get tabs for indentation + ; but spaces for alignment of continuation lines. + (smart-tabs-mode . t) + + ; Multi-line "if" conditions are indented like this: + ; if ( foo + ; && bar) + ; (You have to add the spaces on the first line yourself, but + ; this will make emacs align the "&&" correctly.) + (c-offsets-alist (arglist-cont-nonempty . (nm-lineup-arglist)) + (arglist-close . (nm-lineup-arglist))) + + ; NM's comments use two spaces after a period and are + ; (generally) wrapped at 80 characters + (sentence-end-double-space . t) + (fill-column . 80) + )) + +;; http://www.emacswiki.org/emacs/SmartTabs +(require 'smart-tabs-mode) + +;; The smart-tabs-mode documentation tells you to use +;; smart-tabs-insinuate to set it up, but that will cause it to be +;; enabled for *all* C code. We only want to enable it for +;; NetworkManager, so we have to manually set it up first. +(smart-tabs-advice c-indent-line c-basic-offset) +(smart-tabs-advice c-indent-region c-basic-offset) + + +;; Implements the weird "if" alignment +(defun nm-lineup-arglist (langelem) + (save-excursion + (back-to-indentation) + (c-go-up-list-backward) + (vector (+ (current-column) 1)))) + + +(dir-locals-set-class-variables 'nm '((c-mode . ((c-file-style . "NetworkManager"))))) + +;; Now add a line like the following for every directory where you want the +;; "NetworkManager" style to be the default + +; (dir-locals-set-directory-class "/home/danw/gnome/NetworkManager/" 'nm) +; (dir-locals-set-directory-class "/home/danw/gnome/network-manager-applet/" 'nm) + +(provide 'networkmanager-style) diff --git a/contrib/fedora/REQUIRED_PACKAGES b/contrib/fedora/REQUIRED_PACKAGES new file mode 100755 index 00000000..38ca9026 --- /dev/null +++ b/contrib/fedora/REQUIRED_PACKAGES @@ -0,0 +1,108 @@ +#!/bin/bash + +# A list of packages useful/needed to build and develop +# NetworkManager on Fedora and RHEL. +# +# Not all of these packages are available, depending +# on your distribution/release. But yum will happily +# skip them. +# +# Not all of these packages are strictly speaking necessary. +# This is a generous list of related packages. + +set -xe + +DNF="$(command -v dnf &>/dev/null && echo dnf || echo yum)" + +SUDO= +[ "$EUID" -eq 0 ] || SUDO=sudo + +install() { + if [ "$NM_INSTALL" != "" ]; then + $NM_INSTALL "$@" + else + $SUDO "$DNF" install -y "$@" + fi +} + +install_ignore_missing() { + for p; do + install "$p" || : + done +} + +if test "$NM_NO_EXTRA" != 1; then + # these packages are convenient for developing, but not necessary + # for CI testing. + EXTRA_PACKAGES=( + bash-completion \ + cscope \ + ) +else + EXTRA_PACKAGES=() +fi + +install \ + /usr/bin/clang-format \ + /usr/bin/xargs \ + ModemManager-devel \ + ModemManager-glib-devel \ + audit-libs-devel \ + bluez-libs-devel \ + clang \ + dbus-devel \ + dbus-x11 \ + dhclient \ + firewalld-filesystem \ + gcc-c++ \ + gettext-devel \ + git \ + glib2-doc \ + gnutls-devel \ + gobject-introspection-devel \ + gtk-doc \ + iptables \ + jansson-devel \ + jq \ + libcurl-devel \ + libndp-devel \ + libselinux-devel \ + libtool \ + libuuid-devel \ + make \ + meson \ + mobile-broadband-provider-info-devel \ + newt-devel \ + nss-devel \ + polkit-devel \ + ppp \ + ppp-devel \ + python3-dbus \ + python3-gobject \ + python3-pexpect \ + readline-devel \ + rpm-build \ + systemd-devel \ + teamd-devel \ + util-linux \ + vala \ + valgrind \ + which \ + "${EXTRA_PACKAGES[@]}" + +# some packages don't exist in certain distributions. Install them one-by-one, and ignore errors. +install_ignore_missing \ + black \ + dbus-python \ + iproute-tc \ + libasan \ + libpsl-devel \ + libubsan \ + libvala-devel \ + pexpect \ + pygobject3-base \ + python-gobject-base \ + python36-pexpect \ + qt-devel \ + vala-devel \ + #end diff --git a/contrib/fedora/rpm/00-server.conf b/contrib/fedora/rpm/00-server.conf new file mode 100644 index 00000000..ba3d29fb --- /dev/null +++ b/contrib/fedora/rpm/00-server.conf @@ -0,0 +1,14 @@ +# This configuration file changes NetworkManager's behavior to +# what's expected on "traditional UNIX server" type deployments. +# +# See "man NetworkManager.conf" for more information about these +# and other keys. + +[main] +# Do not do automatic (DHCP/SLAAC) configuration on ethernet devices +# with no other matching connections. +no-auto-default=* + +# Ignore the carrier (cable plugged in) state when attempting to +# activate static-IP connections. +ignore-carrier=* diff --git a/contrib/fedora/rpm/20-connectivity-fedora.conf b/contrib/fedora/rpm/20-connectivity-fedora.conf new file mode 100644 index 00000000..5c9476a0 --- /dev/null +++ b/contrib/fedora/rpm/20-connectivity-fedora.conf @@ -0,0 +1,10 @@ +# Enable connectivity checking for NetworkManager. +# See `man NetworkManager.conf`. +# +# Note that connectivity checking works badly with rp_filter set to +# strict. Check "/proc/sys/net/ipv4/conf/*/rp_filter". +[connectivity] +enabled=true +uri=http://fedoraproject.org/static/hotspot.txt +response=OK +interval=300 diff --git a/contrib/fedora/rpm/20-connectivity-redhat.conf b/contrib/fedora/rpm/20-connectivity-redhat.conf new file mode 100644 index 00000000..070f030b --- /dev/null +++ b/contrib/fedora/rpm/20-connectivity-redhat.conf @@ -0,0 +1,10 @@ +# Enable connectivity checking for NetworkManager. +# See `man NetworkManager.conf`. +# +# Note that connectivity checking works badly with rp_filter set to +# strict. Check "/proc/sys/net/ipv4/conf/*/rp_filter". +[connectivity] +enabled=true +uri=http://static.redhat.com/test/rhel-networkmanager.txt +response=OK +interval=300 diff --git a/contrib/fedora/rpm/22-wifi-mac-addr.conf b/contrib/fedora/rpm/22-wifi-mac-addr.conf new file mode 100644 index 00000000..2e329c88 --- /dev/null +++ b/contrib/fedora/rpm/22-wifi-mac-addr.conf @@ -0,0 +1,31 @@ +# This sets defaults for Wi-Fi profiles to set a generated, stable MAC address. +# +# Do not modify this file. You can hide/overwrite this file by placing a file +# to "/etc/NetworkManager/conf.d/22-wifi-mac-addr.conf". You can also add +# configuration snippets with higher priority that override this setting (see +# `man 5 NetworkManager.conf`). Most importantly, this snippet only sets +# default values for the profile. You can explicitly set the value for each +# profile, so that this default value is not used. +# +# For example, on a particular profile/network set +# +# $ nmcli connection modify "$PROFILE" wifi.cloned-mac-address permanent +# +# to use the hardware MAC address. This prevents the default from this file +# to take effect. +# +# Or +# +# $ nmcli connection modify "$PROFILE" wifi.cloned-mac-address stable connection.stable-id '${NETWORK_SSID}/${BOOT}' +# +# to get a generated MAC address that changes on each boot. Note how setting +# "connection.stable-id" also affects other aspects of the profile. +# +# See `man 5 nm-settings` for "wifi.cloned-mac-address" and "connection.stable-id". + +[connection.22-wifi-mac-addr] +match-device=type:wifi +wifi.cloned-mac-address=stable-ssid + +[.config] +enable=nm-version-min:1.45 diff --git a/contrib/fedora/rpm/70-nm-connectivity.conf b/contrib/fedora/rpm/70-nm-connectivity.conf new file mode 100644 index 00000000..0e4b0e27 --- /dev/null +++ b/contrib/fedora/rpm/70-nm-connectivity.conf @@ -0,0 +1,15 @@ +# The Strict mode of RFC3704 Reverse Path filtering breaks some pretty +# common and reasonable use cases. +# +# Notably, it makes it impossible for NetworkManager to do connectivity +# check on a newly arriving default route (it starts with a higher metric +# and is bumped lower if there's connectivity). +# +# Kernel's default is 0 (no filter), systemd configures a Loose filter since +# commit 230450d4e4f1 ('sysctl.d: switch net.ipv4.conf.all.rp_filter from 1 +# to 2'). However, RHEL systemd package happens to default to Strict mode +# for historic reasons. Let's override it if we're doing connectivity +# checking. + +# Source route verification +net.ipv4.conf.all.rp_filter = 0 diff --git a/contrib/fedora/rpm/NetworkManager.conf b/contrib/fedora/rpm/NetworkManager.conf new file mode 100644 index 00000000..287c9d01 --- /dev/null +++ b/contrib/fedora/rpm/NetworkManager.conf @@ -0,0 +1,52 @@ +# Configuration file for NetworkManager. +# +# See "man 5 NetworkManager.conf" for details. +# +# The directories /usr/lib/NetworkManager/conf.d/ and /run/NetworkManager/conf.d/ +# can contain additional .conf snippets installed by packages. These files are +# read before NetworkManager.conf and have thus lowest priority. +# The directory /etc/NetworkManager/conf.d/ can contain additional .conf +# snippets. Those snippets are merged last and overwrite the settings from this main +# file. +# +# The files within one conf.d/ directory are read in asciibetical order. +# +# You can prevent loading a file /usr/lib/NetworkManager/conf.d/NAME.conf +# by having a file NAME.conf in either /run/NetworkManager/conf.d/ or /etc/NetworkManager/conf.d/. +# Likewise, snippets from /run can be prevented from loading by placing +# a file with the same name in /etc/NetworkManager/conf.d/. +# +# If two files define the same key, the one that is read afterwards will overwrite +# the previous one. + +[main] +#plugins=keyfile,ifcfg-rh + + +[logging] +# When debugging NetworkManager, enabling debug logging is of great help. +# +# Logfiles contain no passwords and little sensitive information. But please +# check before posting the file online. You can also personally hand over the +# logfile to a NM developer to treat it confidential. Meet us on #nm on Libera.Chat. +# +# You can also change the log-level at runtime via +# $ nmcli general logging level TRACE domains ALL +# However, usually it's cleaner to enable debug logging +# in the configuration and restart NetworkManager so that +# debug logging is enabled from the start. +# +# You will find the logfiles in syslog, for example via +# $ journalctl -u NetworkManager +# +# Please post full logfiles for bug reports without pre-filtering or truncation. +# Also, for debugging the entire `journalctl` output can be interesting. Don't +# limit unnecessarily with `journalctl -u`. Exceptions are if you are worried +# about private data. Check before posting logfiles! +# +# Note that debug logging of NetworkManager can be quite verbose. Some messages +# might be rate-limited by the logging daemon (see RateLimitIntervalSec, RateLimitBurst +# in man journald.conf). Please disable rate-limiting before collecting debug logs! +# +#level=TRACE +#domains=ALL diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec new file mode 100644 index 00000000..f55d243d --- /dev/null +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -0,0 +1,1307 @@ +# SPEC file to build NetworkManager for testing. It aims for a similar +# configuration as rhel-7.0 and Fedora rawhide +# +# This spec file is not used as is to create official packages for RHEL, Fedora or any +# other distribution. +# +# Note that it contains __PLACEHOLDERS__ that will be replaced by the accompanying 'build.sh' script. + + +%global wpa_supplicant_version 1:1.1 + +%global ppp_version %(pkg-config --modversion pppd 2>/dev/null || sed -n 's/^#define\\s*VERSION\\s*"\\([^\\s]*\\)"$/\\1/p' %{_includedir}/pppd/patchlevel.h 2>/dev/null | grep . || echo bad) +%global glib2_version %(pkg-config --modversion glib-2.0 2>/dev/null || echo bad) + +%global epoch_version 1 +%global real_version __VERSION__ +%global rpm_version %{real_version} +%global release_version __RELEASE_VERSION__ +%global snapshot __SNAPSHOT__ +%global git_sha __COMMIT__ +%global bcond_default_debug __BCOND_DEFAULT_DEBUG__ +%global bcond_default_lto __BCOND_DEFAULT_LTO__ +%global bcond_default_test __BCOND_DEFAULT_TEST__ + +%global obsoletes_device_plugins 1:0.9.9.95-1 +%global obsoletes_ppp_plugin 1:1.5.3 +%global obsoletes_initscripts_updown 1:1.36.0-0.6 +%global obsoletes_ifcfg_rh 1:1.36.2 + +%global nmlibdir %{_prefix}/lib/%{name} +%global nmplugindir %{_libdir}/%{name}/%{version}-%{release} + +%global _hardened_build 1 + +%if "x%{?snapshot}" != "x" +%global snapshot_dot .%{snapshot} +%endif +%if "x%{?git_sha}" != "x" +%global git_sha_dot .%{git_sha} +%endif + +%global snap %{?snapshot_dot}%{?git_sha_dot} + +%global real_version_major %(printf '%s' '%{real_version}' | sed -n 's/^\\([1-9][0-9]*\\.[0-9][0-9]*\\)\\.[0-9][0-9]*$/\\1/p') + +%global systemd_units NetworkManager.service NetworkManager-wait-online.service NetworkManager-dispatcher.service nm-priv-helper.service + +%global systemd_units_cloud_setup nm-cloud-setup.service nm-cloud-setup.timer + +############################################################################### +%if 0%{?fedora} > 40 +%bcond_without meson +%else +%bcond_with meson +%endif +%bcond_without adsl +%bcond_without bluetooth +%bcond_without wwan +%if 0%{?rhel} >= 10 +%bcond_with team +%else +%bcond_without team +%endif +%bcond_without wifi +%bcond_without ovs +%bcond_without ppp +%bcond_without nmtui +%bcond_without nm_cloud_setup +%bcond_without regen_docs +%if %{bcond_default_debug} +%bcond_without debug +%else +%bcond_with debug +%endif +%if %{bcond_default_test} +%bcond_without test +%else +%bcond_with test +%endif +%if "%{?bcond_default_lto}" == "" +%if 0%{?fedora} >= 33 || 0%{?rhel} >= 9 +%bcond_without lto +%else +%bcond_with lto +%endif +%else +%if %{bcond_default_lto} +%bcond_without lto +%else +%bcond_with lto +%endif +%endif +%bcond_with sanitizer +%if 0%{?fedora} +%bcond_without connectivity_fedora +%else +%bcond_with connectivity_fedora +%endif +%if 0%{?rhel} && 0%{?rhel} >= 8 +%bcond_without connectivity_redhat +%else +%bcond_with connectivity_redhat +%endif +%if 0%{?fedora} >= 29 || 0%{?rhel} >= 8 +%bcond_without crypto_gnutls +%else +%bcond_with crypto_gnutls +%endif +%if 0%{?rhel} +%bcond_with iwd +%else +%bcond_without iwd +%endif +%if 0%{?fedora} >= 32 || 0%{?rhel} >= 8 +%bcond_without firewalld_zone +%else +%bcond_with firewalld_zone +%endif + +############################################################################### + +%if 0%{?fedora} || 0%{?rhel} >= 8 +%global dbus_version 1.9.18 +%global dbus_sys_dir %{_datadir}/dbus-1/system.d +%else +%global dbus_version 1.1 +%global dbus_sys_dir %{_sysconfdir}/dbus-1/system.d +%endif + +# Older libndp versions use select() (rh#1933041). On well known distros, +# choose a version that has the necessary fix. +%if 0%{?rhel} && 0%{?rhel} == 8 +%global libndp_version 1.7-4 +%else +%global libndp_version %{nil} +%endif + +%if %{with bluetooth} || %{with wwan} +%global with_modem_manager_1 1 +%else +%global with_modem_manager_1 0 +%endif + +%if 0%{?fedora} >= 31 || 0%{?rhel} >= 8 +%global dhcp_default internal +%else +%global dhcp_default dhclient +%endif + +%if 0%{?fedora} || 0%{?rhel} >= 8 +%global logging_backend_default journal +%if 0%{?fedora} || 0%{?rhel} >= 9 +%global dns_rc_manager_default auto +%else +%global dns_rc_manager_default symlink +%endif +%else +%global logging_backend_default syslog +%global dns_rc_manager_default file +%endif + +%if 0%{?fedora} >= 33 || 0%{?rhel} >= 9 +%global config_plugins_default_ifcfg_rh 0 +%else +%global config_plugins_default_ifcfg_rh 1 +%endif + +%if 0%{?rhel} >= 10 +%global with_ifcfg_rh 0 +%global split_ifcfg_rh 0 +%elif 0%{?fedora} >= 36 +%global with_ifcfg_rh 1 +%global split_ifcfg_rh 1 +%else +%global with_ifcfg_rh 1 +%global split_ifcfg_rh 0 +%endif + +%if (0%{?fedora} >= 36 && 0%{?fedora} < 39) || 0%{?rhel} == 9 +%global ifcfg_warning 1 +%else +%global ifcfg_warning 0 +%endif + +%if 0%{?fedora} >= 39 +%global ifcfg_migrate 1 +%else +%global ifcfg_migrate 0 +%endif + +%if 0%{?fedora} +# Although eBPF would be available on Fedora's kernel, it seems +# we often get SELinux denials (rh#1651654). But even aside them, +# bpf(BPF_MAP_CREATE, ...) randomly fails with EPERM. That might +# be related to `ulimit -l`. Anyway, this is not usable at the +# moment. +%global ebpf_enabled "no" +%else +%global ebpf_enabled "no" +%endif + +# Fedora 33 enables LTO by default by setting CFLAGS="-flto -ffat-lto-objects". +# However, we also require "-flto -flto-partition=none", so disable Fedora's +# default and use our configure option --with-lto instead. +%define _lto_cflags %{nil} + +############################################################################### + +Name: NetworkManager +Summary: Network connection manager and user applications +Epoch: %{epoch_version} +Version: %{rpm_version} +Release: %{release_version}%{?snap}%{?dist} +Group: System Environment/Base +License: GPL-2.0-or-later AND LGPL-2.1-or-later +URL: https://networkmanager.dev/ + +#Source: https://download.gnome.org/sources/NetworkManager/%{real_version_major}/%{name}-%{real_version}.tar.xz +Source: __SOURCE1__ +Source1: NetworkManager.conf +Source2: 00-server.conf +Source4: 20-connectivity-fedora.conf +Source5: 20-connectivity-redhat.conf +Source6: 22-wifi-mac-addr.conf +Source7: 70-nm-connectivity.conf +Source8: readme-ifcfg-rh.txt +Source9: readme-ifcfg-rh-migrated.txt + +#Patch1: 0001-some.patch + +Requires(post): systemd +%if 0%{?fedora} || 0%{?rhel} >= 8 +Requires(post): systemd-udev +%endif +Requires(post): /usr/sbin/update-alternatives +Requires(preun): systemd +Requires(preun): /usr/sbin/update-alternatives +Requires(postun): systemd + +Requires: dbus >= %{dbus_version} +Requires: glib2 >= %{glib2_version} +Requires: %{name}-libnm%{?_isa} = %{epoch}:%{version}-%{release} +%if "%{libndp_version}" != "" +Requires: libndp >= %{libndp_version} +%endif +Obsoletes: NetworkManager < %{obsoletes_device_plugins} +Obsoletes: NetworkManager < %{obsoletes_ppp_plugin} +Obsoletes: NetworkManager-wimax < 1:1.2 +%if 0%{?rhel} && 0%{?rhel} == 8 +Suggests: NetworkManager-initscripts-updown +%endif +Obsoletes: NetworkManager < %{obsoletes_initscripts_updown} +%if 0%{?split_ifcfg_rh} +Obsoletes: NetworkManager < %{obsoletes_ifcfg_rh} +%endif + +%if 0%{?rhel} && 0%{?rhel} <= 7 +# Kept for RHEL to ensure that wired 802.1x works out of the box +Requires: wpa_supplicant >= 1:1.1 +%endif + +Conflicts: NetworkManager-vpnc < 1:0.7.0.99-1 +Conflicts: NetworkManager-openvpn < 1:0.7.0.99-1 +Conflicts: NetworkManager-pptp < 1:0.7.0.99-1 +Conflicts: NetworkManager-openconnect < 0:0.7.0.99-1 +Conflicts: kde-plasma-networkmanagement < 1:0.9-0.49.20110527git.nm09 + +BuildRequires: make +BuildRequires: gcc +BuildRequires: libtool +BuildRequires: pkgconfig +%if %{with meson} +BuildRequires: meson +%else +BuildRequires: automake +BuildRequires: autoconf +%endif +BuildRequires: gettext-devel >= 0.19.8 + +BuildRequires: dbus-devel >= %{dbus_version} +BuildRequires: glib2-devel >= 2.40.0 +BuildRequires: gobject-introspection-devel >= 0.10.3 +%if %{with ppp} +BuildRequires: ppp-devel >= 2.4.5 +%endif +%if %{with crypto_gnutls} +BuildRequires: gnutls-devel >= 2.12 +%else +BuildRequires: nss-devel >= 3.11.7 +%endif +BuildRequires: readline-devel +BuildRequires: audit-libs-devel +%if %{with regen_docs} +BuildRequires: gtk-doc +%endif +BuildRequires: libudev-devel +BuildRequires: libuuid-devel +BuildRequires: /usr/bin/valac +BuildRequires: libxslt +%if %{with bluetooth} +BuildRequires: bluez-libs-devel +%endif +BuildRequires: systemd >= 200-3 systemd-devel +%if 0%{?fedora} +BuildRequires: libpsl-devel >= 0.1 +%endif +BuildRequires: libcurl-devel +BuildRequires: libndp-devel >= 1.0 +%if 0%{?with_modem_manager_1} +BuildRequires: ModemManager-glib-devel >= 1.0 +%endif +%if %{with wwan} +BuildRequires: mobile-broadband-provider-info-devel +%endif +%if %{with nmtui} +BuildRequires: newt-devel +%endif +BuildRequires: /usr/bin/dbus-launch +BuildRequires: python3 +BuildRequires: python3-gobject-base +BuildRequires: python3-dbus +BuildRequires: python3-pexpect +BuildRequires: libselinux-devel +BuildRequires: polkit-devel +BuildRequires: jansson-devel +%if %{with sanitizer} +BuildRequires: libasan +%if 0%{?fedora} || 0%{?rhel} >= 8 +BuildRequires: libubsan +%endif +%endif +%if %{with firewalld_zone} +BuildRequires: firewalld-filesystem +%endif +BuildRequires: iproute +%if 0%{?fedora} || 0%{?rhel} >= 8 +BuildRequires: iproute-tc +%endif + +Provides: %{name}-dispatcher%{?_isa} = %{epoch}:%{version}-%{release} + +# NetworkManager uses various parts of systemd-networkd internally, including +# DHCP client, IPv4 Link-Local address negotiation or LLDP support. +# This provide is essentially here so that NetworkManager shows on Security +# Response Team's radar in case a flaw is found. The code is frequently +# synchronized and thus it's not easy to establish a good version number +# here. The version of zero is there just to have something conservative so +# that the scripts that would parse the SPEC file naively would be unlikely +# to fail. Refer to git log for the real date and commit number of last +# synchronization: +# https://gitlab.freedesktop.org/NetworkManager/NetworkManager/commits/main/src/ +Provides: bundled(systemd) = 0 + + +%description +NetworkManager is a system service that manages network interfaces and +connections based on user or automatic configuration. It supports +Ethernet, Bridge, Bond, VLAN, Team, InfiniBand, Wi-Fi, mobile broadband +(WWAN), PPPoE and other devices, and supports a variety of different VPN +services. + + +%if %{with adsl} +%package adsl +Summary: ADSL device plugin for NetworkManager +Group: System Environment/Base +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} +Obsoletes: NetworkManager < %{obsoletes_device_plugins} + +%description adsl +This package contains NetworkManager support for ADSL devices. +%endif + + +%if %{with bluetooth} +%package bluetooth +Summary: Bluetooth device plugin for NetworkManager +Group: System Environment/Base +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} +Requires: NetworkManager-wwan = %{epoch}:%{version}-%{release} +%if 0%{?rhel} && 0%{?rhel} <= 7 +# No Requires:bluez to prevent it being installed when updating +# to the split NM package +%else +Requires: bluez >= 4.101-5 +%endif +Obsoletes: NetworkManager < %{obsoletes_device_plugins} + +%description bluetooth +This package contains NetworkManager support for Bluetooth devices. +%endif + + +%if %{with team} +%package team +Summary: Team device plugin for NetworkManager +Group: System Environment/Base +BuildRequires: teamd-devel +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} +Obsoletes: NetworkManager < %{obsoletes_device_plugins} +%if 0%{?fedora} || 0%{?rhel} >= 8 +# Team was split from main NM binary between 0.9.10 and 1.0 +# We need this Obsoletes in addition to the one above +# (git:3aede801521ef7bff039e6e3f1b3c7b566b4338d). +Obsoletes: NetworkManager < 1:1.0.0 +%endif + +%description team +This package contains NetworkManager support for team devices. +%endif + + +%if %{with wifi} +%package wifi +Summary: Wifi plugin for NetworkManager +Group: System Environment/Base +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} + +%if 0%{?fedora} >= 29 || 0%{?rhel} >= 9 +Requires: wireless-regdb +%else +Requires: crda +%endif + +%if %{with iwd} && (0%{?fedora} >= 25 || 0%{?rhel} >= 8) +Requires: (wpa_supplicant >= %{wpa_supplicant_version} or iwd) +Suggests: wpa_supplicant +%else +# Just require wpa_supplicant on platforms that don't support boolean +# dependencies even though the plugin supports both supplicant and +# iwd backend. +Requires: wpa_supplicant >= %{wpa_supplicant_version} +%endif + +Obsoletes: NetworkManager < %{obsoletes_device_plugins} + +%description wifi +This package contains NetworkManager support for Wifi and OLPC devices. +%endif + + +%if %{with wwan} +%package wwan +Summary: Mobile broadband device plugin for NetworkManager +Group: System Environment/Base +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} +%if 0%{?rhel} && 0%{?rhel} <= 7 +# No Requires:ModemManager to prevent it being installed when updating +# to the split NM package +%else +Requires: ModemManager +%endif +Obsoletes: NetworkManager < %{obsoletes_device_plugins} + +%description wwan +This package contains NetworkManager support for mobile broadband (WWAN) +devices. +%endif + + +%if %{with ovs} +%package ovs +Summary: Open vSwitch device plugin for NetworkManager +Group: System Environment/Base +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} +%if 0%{?rhel} == 0 +Requires: openvswitch +%endif + +%description ovs +This package contains NetworkManager support for Open vSwitch bridges. +%endif + + +%if %{with ppp} +%package ppp +Summary: PPP plugin for NetworkManager +Group: System Environment/Base +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} +Requires: ppp = %{ppp_version} +Requires: NetworkManager = %{epoch}:%{version}-%{release} +Obsoletes: NetworkManager < %{obsoletes_ppp_plugin} + +%description ppp +This package contains NetworkManager support for PPP. +%endif + + +%package libnm +Summary: Libraries for adding NetworkManager support to applications. +Group: Development/Libraries +Conflicts: NetworkManager-glib < 1:1.31.0 +License: LGPL-2.1-or-later + +%description libnm +This package contains the libraries that make it easier to use some +NetworkManager functionality from applications. + + +%package libnm-devel +Summary: Header files for adding NetworkManager support to applications. +Group: Development/Libraries +Requires: %{name}-libnm%{?_isa} = %{epoch}:%{version}-%{release} +Requires: glib2-devel +Requires: pkgconfig +License: LGPL-2.1-or-later + +%description libnm-devel +This package contains the header and pkg-config files for development +applications using NetworkManager functionality from applications. + + +%if %{with connectivity_fedora} +%package config-connectivity-fedora +Summary: NetworkManager config file for connectivity checking via Fedora servers +Group: System Environment/Base +BuildArch: noarch +Provides: NetworkManager-config-connectivity = %{epoch}:%{version}-%{release} + +%description config-connectivity-fedora +This adds a NetworkManager configuration file to enable connectivity checking +via Fedora infrastructure. +%endif + + +%if %{with connectivity_redhat} +%package config-connectivity-redhat +Summary: NetworkManager config file for connectivity checking via Red Hat servers +Group: System Environment/Base +BuildArch: noarch +Provides: NetworkManager-config-connectivity = %{epoch}:%{version}-%{release} + +%description config-connectivity-redhat +This adds a NetworkManager configuration file to enable connectivity checking +via Red Hat infrastructure. +%endif + + +%package config-server +Summary: NetworkManager config file for "server-like" defaults +Group: System Environment/Base +BuildArch: noarch + +%description config-server +This adds a NetworkManager configuration file to make it behave more +like the old "network" service. In particular, it stops NetworkManager +from automatically running DHCP on unconfigured ethernet devices, and +allows connections with static IP addresses to be brought up even on +ethernet devices with no carrier. + +This package is intended to be installed by default for server +deployments. + + +%if %{?with_ifcfg_rh} +%package dispatcher-routing-rules +Summary: NetworkManager dispatcher file for advanced routing rules +Group: System Environment/Base +%if 0%{?split_ifcfg_rh} +Requires: %{name}-initscripts-ifcfg-rh +%endif +BuildArch: noarch +Provides: %{name}-config-routing-rules = %{epoch}:%{version}-%{release} +Obsoletes: %{name}-config-routing-rules < 1:1.31.0 + +%description dispatcher-routing-rules +This adds a NetworkManager dispatcher file to support networking +configurations using "/etc/sysconfig/network-scripts/rule-NAME" files +(eg, to do policy-based routing). +%endif + + +%if %{with nmtui} +%package tui +Summary: NetworkManager curses-based UI +Group: System Environment/Base +Requires: %{name} = %{epoch}:%{version}-%{release} +Requires: %{name}-libnm%{?_isa} = %{epoch}:%{version}-%{release} + +%description tui +This adds a curses-based "TUI" (Text User Interface) to +NetworkManager, to allow performing some of the operations supported +by nm-connection-editor and nm-applet in a non-graphical environment. +%endif + + +%if 0%{?split_ifcfg_rh} +%package initscripts-ifcfg-rh +Summary: NetworkManager plugin for reading and writing connections in ifcfg-rh format +Group: System Environment/Base +Requires: %{name} = %{epoch}:%{version}-%{release} +Obsoletes: NetworkManager < %{obsoletes_ifcfg_rh} + +%description initscripts-ifcfg-rh +Installs a plugin for reading and writing connection profiles using +the Red Hat ifcfg format in /etc/sysconfig/network-scripts/. +%endif + + +%if %{with nm_cloud_setup} +%package cloud-setup +Summary: Automatically configure NetworkManager in cloud +Group: System Environment/Base +Requires: %{name} = %{epoch}:%{version}-%{release} +Requires: %{name}-libnm%{?_isa} = %{epoch}:%{version}-%{release} + +%description cloud-setup +Installs a nm-cloud-setup tool that can automatically configure +NetworkManager in cloud environment. Only certain cloud providers +like Aliyun, Azure, EC2, GCP are supported. +%endif + + +%if %{?with_ifcfg_rh} +%package initscripts-updown +Summary: Legacy ifup/ifdown scripts for NetworkManager that replace initscripts (network-scripts) +Group: System Environment/Base +BuildArch: noarch +Requires: NetworkManager +Requires: /usr/bin/nmcli +Obsoletes: NetworkManager < %{obsoletes_initscripts_updown} + +%description initscripts-updown +Installs alternative ifup/ifdown scripts that talk to NetworkManager. +This is only for backward compatibility with initscripts (network-scripts). +Preferably use nmcli instead. +%endif + +%prep +%autosetup -p1 -n NetworkManager-%{real_version} + + +%build +%if %{with meson} +%meson \ + -Db_ndebug=false \ + --warnlevel 2 \ +%if %{with test} + --werror \ +%endif + -Dnft=%{_sbindir}/nft \ + -Diptables=%{_sbindir}/iptables \ + -Ddhclient=%{_sbindir}/dhclient \ + -Ddhcpcanon=no \ + -Ddhcpcd=no \ + -Dconfig_dhcp_default=%{dhcp_default} \ +%if %{with crypto_gnutls} + -Dcrypto=gnutls \ +%else + -Dcrypto=nss \ +%endif +%if %{with debug} + -Dmore_logging=true \ + -Dmore_asserts=10000 \ +%else + -Dmore_logging=false \ + -Dmore_asserts=0 \ +%endif + -Dld_gc=true \ +%if %{with lto} + -D b_lto=true \ +%else + -D b_lto=false \ +%endif + -Dlibaudit=yes-disabled-by-default \ +%if 0%{?with_modem_manager_1} + -Dmodem_manager=true \ +%else + -Dmodem_manager=false \ +%endif +%if %{with wifi} + -Dwifi=true \ +%if 0%{?fedora} + -Dwext=true \ +%else + -Dwext=false \ +%endif +%else + -Dwifi=false \ +%endif +%if %{with iwd} + -Diwd=true \ +%else + -Diwd=false \ +%endif +%if %{with bluetooth} + -Dbluez5_dun=true \ +%else + -Dbluez5_dun=false \ +%endif +%if %{with nmtui} + -Dnmtui=true \ +%else + -Dnmtui=false \ +%endif +%if %{with nm_cloud_setup} + -Dnm_cloud_setup=true \ +%else + -Dnm_cloud_setup=false \ +%endif + -Dvapi=true \ + -Dintrospection=true \ +%if %{with regen_docs} + -Ddocs=true \ +%else + -Ddocs=false \ +%endif + -Dqt=false \ +%if %{with team} + -Dteamdctl=true \ +%else + -Dteamdctl=false \ +%endif +%if %{with ovs} + -Dovs=true \ +%else + -Dovs=false \ +%endif + -Dselinux=true \ + -Dpolkit=true \ + -Dconfig_auth_polkit_default=true \ + -Dmodify_system=true \ + -Dconcheck=true \ +%if 0%{?fedora} + -Dlibpsl=true \ +%else + -Dlibpsl=false \ +%endif +%if %{ebpf_enabled} != "yes" + -Debpf=false \ +%else + -Debpf=true \ +%endif + -Dsession_tracking=systemd \ + -Dsuspend_resume=systemd \ + -Dsystemdsystemunitdir=%{_unitdir} \ + -Dsystem_ca_path=/etc/pki/tls/cert.pem \ + -Ddbus_conf_dir=%{dbus_sys_dir} \ + -Dtests=yes \ + -Dvalgrind=no \ +%if %{?with_ifcfg_rh} + -Difcfg_rh=true \ +%else + -Difcfg_rh=false \ +%endif + -Difupdown=false \ +%if %{with ppp} + -Dppp=true \ + -Dpppd="%{_sbindir}/pppd" \ + -Dpppd_plugin_dir="%{_libdir}/pppd/%{ppp_version}" \ +%else + -Dppp=false \ +%endif +%if %{with firewalld_zone} + -Dfirewalld_zone=true \ +%else + -Dfirewalld_zone=false \ +%endif + -Ddist_version=%{version}-%{release} \ +%if %{?config_plugins_default_ifcfg_rh} + -Dconfig_plugins_default=ifcfg-rh \ +%endif +%if %{?ifcfg_migrate} + -Dconfig_migrate_ifcfg_rh_default=true \ +%endif + -Dresolvconf=no \ + -Dnetconfig=no \ + -Dconfig_dns_rc_manager_default=%{dns_rc_manager_default} \ + -Dconfig_logging_backend_default=%{logging_backend_default} + +%meson_build + +%else +# autotools +%if %{with regen_docs} +gtkdocize +%endif +autoreconf --install --force +%configure \ + --with-runstatedir=%{_rundir} \ + --enable-silent-rules=no \ + --enable-static=no \ + --with-nft=%{_sbindir}/nft \ + --with-iptables=%{_sbindir}/iptables \ + --with-dhclient=%{_sbindir}/dhclient \ + --with-dhcpcd=no \ + --with-dhcpcanon=no \ + --with-config-dhcp-default=%{dhcp_default} \ +%if %{with crypto_gnutls} + --with-crypto=gnutls \ +%else + --with-crypto=nss \ +%endif +%if %{with sanitizer} + --with-address-sanitizer=exec \ +%if 0%{?fedora} || 0%{?rhel} >= 8 + --enable-undefined-sanitizer=yes \ +%else + --enable-undefined-sanitizer=no \ +%endif +%else + --with-address-sanitizer=no \ + --enable-undefined-sanitizer=no \ +%endif +%if %{with debug} + --enable-more-logging=yes \ + --with-more-asserts=10000 \ +%else + --enable-more-logging=no \ + --with-more-asserts=0 \ +%endif + --enable-ld-gc=yes \ +%if %{with lto} + --enable-lto=yes \ +%else + --enable-lto=no \ +%endif + --with-libaudit=yes-disabled-by-default \ +%if 0%{?with_modem_manager_1} + --with-modem-manager-1=yes \ +%else + --with-modem-manager-1=no \ +%endif +%if %{with wifi} + --enable-wifi=yes \ +%if 0%{?fedora} + --with-wext=yes \ +%else + --with-wext=no \ +%endif +%else + --enable-wifi=no \ +%endif +%if %{with iwd} + --with-iwd=yes \ +%else + --with-iwd=no \ +%endif +%if %{with bluetooth} + --enable-bluez5-dun=yes \ +%else + --enable-bluez5-dun=no \ +%endif +%if %{with nmtui} + --with-nmtui=yes \ +%else + --with-nmtui=no \ +%endif +%if %{with nm_cloud_setup} + --with-nm-cloud-setup=yes \ +%else + --with-nm-cloud-setup=no \ +%endif + --enable-vala=yes \ + --enable-introspection=yes \ +%if %{with regen_docs} + --enable-gtk-doc=yes \ +%else + --enable-gtk-doc=no \ +%endif +%if %{with team} + --enable-teamdctl=yes \ +%else + --enable-teamdctl=no \ +%endif +%if %{with ovs} + --enable-ovs=yes \ +%else + --enable-ovs=no \ +%endif + --with-selinux=yes \ + --enable-polkit=yes \ + --enable-modify-system=yes \ + --enable-concheck=yes \ +%if 0%{?fedora} + --with-libpsl=yes \ +%else + --with-libpsl=no \ +%endif + --with-ebpf=%{ebpf_enabled} \ + --with-session-tracking=systemd \ + --with-suspend-resume=systemd \ + --with-systemdsystemunitdir=%{_unitdir} \ + --with-system-ca-path=/etc/pki/tls/cert.pem \ + --with-dbus-sys-dir=%{dbus_sys_dir} \ + --with-tests=yes \ +%if %{with test} + --enable-more-warnings=error \ +%else + --enable-more-warnings=yes \ +%endif + --with-valgrind=no \ +%if %{?with_ifcfg_rh} + --enable-ifcfg-rh=yes \ +%else + --enable-ifcfg-rh=no \ +%endif + --enable-ifupdown=no \ +%if %{with ppp} + --enable-ppp=yes \ + --with-pppd="%{_sbindir}/pppd" \ + --with-pppd-plugin-dir="%{_libdir}/pppd/%{ppp_version}" \ +%else + --enable-ppp=no \ +%endif +%if %{with firewalld_zone} + --enable-firewalld-zone=yes \ +%else + --enable-firewalld-zone=no \ +%endif + --with-dist-version=%{version}-%{release} \ +%if %{?config_plugins_default_ifcfg_rh} + --with-config-plugins-default=ifcfg-rh \ +%endif +%if %{?ifcfg_migrate} + --with-config-migrate-ifcfg-rh-default=yes \ +%endif + --with-resolvconf=no \ + --with-netconfig=no \ + --with-config-dns-rc-manager-default=%{dns_rc_manager_default} \ + --with-config-logging-backend-default=%{logging_backend_default} + +%make_build + +%endif + +%install +%if %{with meson} +%meson_install +%else +%make_install +%endif + +cp %{SOURCE1} %{buildroot}%{_sysconfdir}/%{name}/ + +cp %{SOURCE2} %{buildroot}%{nmlibdir}/conf.d/ + +%if %{with connectivity_fedora} +cp %{SOURCE4} %{buildroot}%{nmlibdir}/conf.d/ +%endif + +%if %{with connectivity_redhat} +cp %{SOURCE5} %{buildroot}%{nmlibdir}/conf.d/ +mkdir -p %{buildroot}%{_sysctldir} +cp %{SOURCE7} %{buildroot}%{_sysctldir} +%endif + +%if 0%{?fedora} >= 40 +cp %{SOURCE6} %{buildroot}%{nmlibdir}/conf.d/ +%endif + +%if 0%{?ifcfg_warning} +cp %{SOURCE8} %{buildroot}%{_sysconfdir}/sysconfig/network-scripts +%endif +%if 0%{?ifcfg_migrate} +cp %{SOURCE9} %{buildroot}%{_sysconfdir}/sysconfig/network-scripts/readme-ifcfg-rh.txt +%endif + +%if %{?with_ifcfg_rh} +cp examples/dispatcher/10-ifcfg-rh-routes.sh %{buildroot}%{nmlibdir}/dispatcher.d/ +ln -s ../no-wait.d/10-ifcfg-rh-routes.sh %{buildroot}%{nmlibdir}/dispatcher.d/pre-up.d/ +ln -s ../10-ifcfg-rh-routes.sh %{buildroot}%{nmlibdir}/dispatcher.d/no-wait.d/ +%endif + +%find_lang %{name} + +rm -f %{buildroot}%{_libdir}/*.la +rm -f %{buildroot}%{_libdir}/pppd/%{ppp_version}/*.la +rm -f %{buildroot}%{nmplugindir}/*.la + +# Ensure the documentation timestamps are constant to avoid multilib conflicts +find %{buildroot}%{_datadir}/gtk-doc -exec touch --reference configure.ac '{}' \+ + +%if 0%{?__debug_package} && ! 0%{?flatpak} +mkdir -p %{buildroot}%{_prefix}/src/debug/NetworkManager-%{real_version} +cp valgrind.suppressions %{buildroot}%{_prefix}/src/debug/NetworkManager-%{real_version} +%endif + +%if %{?with_ifcfg_rh} +touch %{buildroot}%{_sbindir}/ifup +touch %{buildroot}%{_sbindir}/ifdown +%endif + +%check +%if %{with meson} +%if %{with test} +%meson_test +%else +%ninja_test -C %{_vpath_builddir} || : +%endif +%else +# autotools +%if %{with test} +make -k %{?_smp_mflags} check +%else +make -k %{?_smp_mflags} check || : +%endif +%endif + + +%pre +if [ -f "%{_unitdir}/network-online.target.wants/NetworkManager-wait-online.service" ] ; then + # older versions used to install this file, effectively always enabling + # NetworkManager-wait-online.service. We no longer do that and rely on + # preset. + # But on package upgrade we must explicitly enable it (rh#1455704). + systemctl enable NetworkManager-wait-online.service || : +fi + + +%post +# skip triggering if udevd isn't even accessible, e.g. containers or +# rpm-ostree-based systems +if [ -S /run/udev/control ]; then + /usr/bin/udevadm control --reload-rules || : + /usr/bin/udevadm trigger --subsystem-match=net || : +fi +%if %{with firewalld_zone} +%firewalld_reload +%endif + +%systemd_post %{systemd_units} + + +%if %{?with_ifcfg_rh} +%post initscripts-updown +if [ -f %{_sbindir}/ifup -a ! -L %{_sbindir}/ifup ]; then + # initscripts package too old, won't let us set an alternative + /usr/sbin/update-alternatives --remove ifup %{_libexecdir}/nm-ifup >/dev/null 2>&1 || : +else + /usr/sbin/update-alternatives --install %{_sbindir}/ifup ifup %{_libexecdir}/nm-ifup 50 \ + --slave %{_sbindir}/ifdown ifdown %{_libexecdir}/nm-ifdown +fi +%endif + + +%if %{with nm_cloud_setup} +%post cloud-setup +%systemd_post %{systemd_units_cloud_setup} +%endif + + +%preun +if [ $1 -eq 0 ]; then + # Package removal, not upgrade + /bin/systemctl --no-reload disable NetworkManager.service >/dev/null 2>&1 || : + + # Don't kill networking entirely just on package remove + #/bin/systemctl stop NetworkManager.service >/dev/null 2>&1 || : +fi +%systemd_preun NetworkManager-wait-online.service NetworkManager-dispatcher.service nm-priv-helper.service + + +%if %{?with_ifcfg_rh} +%preun initscripts-updown +if [ $1 -eq 0 ]; then + /usr/sbin/update-alternatives --remove ifup %{_libexecdir}/nm-ifup >/dev/null 2>&1 || : +fi +%endif + + +%if %{with nm_cloud_setup} +%preun cloud-setup +%systemd_preun %{systemd_units_cloud_setup} +%endif + + +%postun +/usr/bin/udevadm control --reload-rules || : +/usr/bin/udevadm trigger --subsystem-match=net || : +%if %{with firewalld_zone} +%firewalld_reload +%endif + +%systemd_postun %{systemd_units} + + +%if (0%{?fedora} && 0%{?fedora} < 28) || 0%{?rhel} +%post libnm -p /sbin/ldconfig +%postun libnm -p /sbin/ldconfig +%endif + + +%if %{with nm_cloud_setup} +%postun cloud-setup +%systemd_postun %{systemd_units_cloud_setup} +%endif + + +%files +%{dbus_sys_dir}/org.freedesktop.NetworkManager.conf +%{dbus_sys_dir}/nm-dispatcher.conf +%{dbus_sys_dir}/nm-priv-helper.conf +%if %{?with_ifcfg_rh} && 0%{?split_ifcfg_rh} == 0 +%{dbus_sys_dir}/nm-ifcfg-rh.conf +%endif +%{_sbindir}/%{name} +%{_bindir}/nmcli +%{_datadir}/bash-completion/completions/nmcli +%dir %{_sysconfdir}/%{name} +%dir %{_sysconfdir}/%{name}/conf.d +%dir %{_sysconfdir}/%{name}/dispatcher.d +%dir %{_sysconfdir}/%{name}/dispatcher.d/pre-down.d +%dir %{_sysconfdir}/%{name}/dispatcher.d/pre-up.d +%dir %{_sysconfdir}/%{name}/dispatcher.d/no-wait.d +%dir %{_sysconfdir}/%{name}/dnsmasq.d +%dir %{_sysconfdir}/%{name}/dnsmasq-shared.d +%dir %{_sysconfdir}/%{name}/system-connections +%config(noreplace) %{_sysconfdir}/%{name}/NetworkManager.conf +%if 0%{?fedora} >= 40 +%{nmlibdir}/conf.d/22-wifi-mac-addr.conf +%endif +%ghost %{_sysconfdir}/%{name}/VPN +%{_bindir}/nm-online +%{_libexecdir}/nm-dhcp-helper +%{_libexecdir}/nm-dispatcher +%{_libexecdir}/nm-initrd-generator +%{_libexecdir}/nm-daemon-helper +%{_libexecdir}/nm-priv-helper +%dir %{_libdir}/%{name} +%dir %{nmplugindir} +%if %{?with_ifcfg_rh} && 0%{?split_ifcfg_rh} == 0 +%{nmplugindir}/libnm-settings-plugin-ifcfg-rh.so +%endif +%if %{with nmtui} +%exclude %{_mandir}/man1/nmtui* +%endif +%dir %{nmlibdir} +%dir %{nmlibdir}/conf.d +%dir %{nmlibdir}/dispatcher.d +%dir %{nmlibdir}/dispatcher.d/pre-down.d +%dir %{nmlibdir}/dispatcher.d/pre-up.d +%dir %{nmlibdir}/dispatcher.d/no-wait.d +%dir %{nmlibdir}/VPN +%dir %{nmlibdir}/system-connections +%{_mandir}/man1/* +%{_mandir}/man5/* +%{_mandir}/man7/nmcli-examples.7* +%{_mandir}/man8/nm-initrd-generator.8* +%{_mandir}/man8/NetworkManager.8* +%{_mandir}/man8/NetworkManager-dispatcher.8* +%{_mandir}/man8/NetworkManager-wait-online.service.8* +%dir %{_localstatedir}/lib/NetworkManager +%if %{?with_ifcfg_rh} +%dir %{_sysconfdir}/sysconfig/network-scripts +%endif +%{_datadir}/dbus-1/system-services/org.freedesktop.nm_dispatcher.service +%{_datadir}/dbus-1/system-services/org.freedesktop.nm_priv_helper.service +%{_datadir}/polkit-1/actions/*.policy +%{_prefix}/lib/udev/rules.d/*.rules +%if %{with firewalld_zone} +%{_prefix}/lib/firewalld/zones/nm-shared.xml +%endif +# systemd stuff +%{_unitdir}/NetworkManager.service +%{_unitdir}/NetworkManager-wait-online.service +%{_unitdir}/NetworkManager-dispatcher.service +%{_unitdir}/nm-priv-helper.service +%dir %{_datadir}/doc/NetworkManager/examples +%{_datadir}/doc/NetworkManager/examples/server.conf +%if 0%{?ifcfg_warning} || 0%{?ifcfg_migrate} +%{_sysconfdir}/sysconfig/network-scripts/readme-ifcfg-rh.txt +%endif +%doc NEWS AUTHORS README.md CONTRIBUTING.md +%license COPYING +%license COPYING.LGPL +%license COPYING.GFDL + + +%if %{with adsl} +%files adsl +%{nmplugindir}/libnm-device-plugin-adsl.so +%else +%exclude %{nmplugindir}/libnm-device-plugin-adsl.so +%endif + + +%if %{with bluetooth} +%files bluetooth +%{nmplugindir}/libnm-device-plugin-bluetooth.so +%endif + + +%if %{with team} +%files team +%{nmplugindir}/libnm-device-plugin-team.so +%endif + + +%if %{with wifi} +%files wifi +%{nmplugindir}/libnm-device-plugin-wifi.so +%endif + + +%if %{with wwan} +%files wwan +%{nmplugindir}/libnm-device-plugin-wwan.so +%{nmplugindir}/libnm-wwan.so +%endif + + +%if %{with ovs} +%files ovs +%{nmplugindir}/libnm-device-plugin-ovs.so +%{_unitdir}/NetworkManager.service.d/NetworkManager-ovs.conf +%{_mandir}/man7/nm-openvswitch.7* +%endif + + +%if %{with ppp} +%files ppp +%{_libdir}/pppd/%{ppp_version}/nm-pppd-plugin.so +%{nmplugindir}/libnm-ppp-plugin.so +%endif + + +%files libnm -f %{name}.lang +%{_libdir}/libnm.so.* +%{_libdir}/girepository-1.0/NM-1.0.typelib + + +%files libnm-devel +%dir %{_includedir}/libnm +%{_includedir}/libnm/*.h +%{_libdir}/pkgconfig/libnm.pc +%{_libdir}/libnm.so +%{_datadir}/gir-1.0/NM-1.0.gir +%dir %{_datadir}/gtk-doc/html/libnm +%{_datadir}/gtk-doc/html/libnm/* +%dir %{_datadir}/gtk-doc/html/NetworkManager +%{_datadir}/gtk-doc/html/NetworkManager/* +%{_datadir}/vala/vapi/libnm.deps +%{_datadir}/vala/vapi/libnm.vapi +%{_datadir}/dbus-1/interfaces/*.xml + + +%if %{with connectivity_fedora} +%files config-connectivity-fedora +%dir %{nmlibdir} +%dir %{nmlibdir}/conf.d +%{nmlibdir}/conf.d/20-connectivity-fedora.conf +%endif + + +%if %{with connectivity_redhat} +%files config-connectivity-redhat +%dir %{nmlibdir} +%dir %{nmlibdir}/conf.d +%{nmlibdir}/conf.d/20-connectivity-redhat.conf +%{_sysctldir}/70-nm-connectivity.conf +%endif + + +%files config-server +%dir %{nmlibdir} +%dir %{nmlibdir}/conf.d +%{nmlibdir}/conf.d/00-server.conf + + +%if %{?with_ifcfg_rh} +%files dispatcher-routing-rules +%{nmlibdir}/dispatcher.d/10-ifcfg-rh-routes.sh +%{nmlibdir}/dispatcher.d/no-wait.d/10-ifcfg-rh-routes.sh +%{nmlibdir}/dispatcher.d/pre-up.d/10-ifcfg-rh-routes.sh +%endif + +%if %{with nmtui} +%files tui +%{_bindir}/nmtui +%{_bindir}/nmtui-edit +%{_bindir}/nmtui-connect +%{_bindir}/nmtui-hostname +%{_mandir}/man1/nmtui* +%endif + + +%if 0%{?split_ifcfg_rh} +%files initscripts-ifcfg-rh +%{nmplugindir}/libnm-settings-plugin-ifcfg-rh.so +%{dbus_sys_dir}/nm-ifcfg-rh.conf +%endif + + +%if %{with nm_cloud_setup} +%files cloud-setup +%{_libexecdir}/nm-cloud-setup +%{_unitdir}/nm-cloud-setup.service +%{_unitdir}/nm-cloud-setup.timer +%{nmlibdir}/dispatcher.d/90-nm-cloud-setup.sh +%{nmlibdir}/dispatcher.d/no-wait.d/90-nm-cloud-setup.sh +%{nmlibdir}/dispatcher.d/pre-up.d/90-nm-cloud-setup.sh +%{_mandir}/man8/nm-cloud-setup.8* +%endif + + +%if %{?with_ifcfg_rh} +%files initscripts-updown +%{_libexecdir}/nm-ifup +%ghost %attr(755, root, root) %{_sbindir}/ifup +%{_libexecdir}/nm-ifdown +%ghost %attr(755, root, root) %{_sbindir}/ifdown +%endif + + +%changelog +__CHANGELOG__ diff --git a/contrib/fedora/rpm/README b/contrib/fedora/rpm/README new file mode 100644 index 00000000..7982a1ef --- /dev/null +++ b/contrib/fedora/rpm/README @@ -0,0 +1,27 @@ +# To build RPM packages for Fedora derivates directly from git, just do: + + +# +# preparation: +# +git clone https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git +cd NetworkManager +git checkout $WHATEVER +./contrib/fedora/REQUIRED_PACKAGES + + +# +# build the packages. Pass --help for usage help. +# +./contrib/fedora/rpm/build_clean.sh + + +# +# install +# +sudo dnf install ./contrib/fedora/rpm/latest/RPMS/x86_64/*rpm + + + +# To generate a clean build from git using mock, run: +./contrib/fedora/rpm/mockbuild.sh diff --git a/contrib/fedora/rpm/build.sh b/contrib/fedora/rpm/build.sh new file mode 100755 index 00000000..8160b915 --- /dev/null +++ b/contrib/fedora/rpm/build.sh @@ -0,0 +1,288 @@ +#!/bin/bash + +#set -vx + +# Set arguments via environment variables. +# Argument can be omitted and defaults will be detected. +# +# BUILDTYPE=|SRPM +# NM_RPMBUILD_ARGS=<additional argus for rpmbuild> +# RELEASE_VERSION= +# SNAPSHOT= +# VERSION= +# COMMIT_FULL= +# COMMIT= +# USERNAME= +# SPECFILE= +# SOURCE=<path> +# SOURCE_FROM_GIT=|1|0 +# SOURCE_NETWORKMANAGER_CONF= +# SOURCE_CONFIG_SERVER= +# SOURCE_CONFIG_CONNECTIVITY_FEDORA= +# SOURCE_CONFIG_CONNECTIVITY_REDHAT= +# SOURCE_SYSCTL_RP_FILTER_REDHAT= +# SOURCE_README_IFCFG_FILES= +# SOURCE_README_IFCFG_MIGRATED= +# SIGN_SOURCE= +# DO_RELEASE= +# BCOND_DEFAULT_DEBUG= +# BCOND_DEFAULT_LTO= +# BCOND_DEFAULT_TEST= + +die() { + echo "$*" >&2 + exit 1 +} + +# copy output also to logfile +LOG() { + echo "$*" +} + +coerce_bool() { + case "$1" in + no|n|NO|N|0) + echo 0 + ;; + yes|y|YES|Y|1) + echo 1 + ;; + "") + printf '%s' "$2" + ;; + esac +} + +in_set() { + local v="$1" + shift + for v2; do + test "$v" = "$v2" && return 0 + done + return 1 +} + +abs_path() { + local F="$1" + + if [[ "$F" != "" ]]; then + F="$(cd "$ORIGDIR" && readlink -f "$F")" || exit 55 + [[ -f "$F" ]] || exit 55 + else + F="$2" + fi + printf '%s' "$F" + exit 0 +} + +get_version() { + local major minor micro + local F="${1:-"$GITDIR/configure.ac"}" + + vars="$(sed -n 's/^m4_define(\[nm_\(major\|minor\|micro\)_version\], *\[\([0-9]\+\)\]) *$/local \1='\''\2'\''/p' "$F" 2>/dev/null)" + eval "$vars" + + [[ -n "$major" && -n "$minor" && "$micro" ]] || return 1 + echo "$major.$minor.$micro" +} + +write_changelog() { + if [[ "x$CHANGELOG" == x ]]; then + cat <<- EOF + * $(LC_TIME=C date '+%a %b %d %Y') $USERNAME - %{epoch_version}:%{version}-%{release_version}%{?snap} + - build of NetworkManager ($DATE, uuid: $UUID, git: $COMMIT_FULL) + $(git log -n20 --date=local --format='- %h %s [%an] (%ci)') + - ... + EOF + else + echo "$CHANGELOG" + fi > "$TEMP/SOURCES/CHANGELOG" +} + +ORIGDIR="$(readlink -f "$PWD")" +SCRIPTDIR="$(dirname "$(readlink -f "$0")")" +LOG "Change to directory \"$SCRIPTDIR\"" +cd "$SCRIPTDIR" || die "could not change into $SCRIPTDIR" +GITDIR="$(cd "$SCRIPTDIR" && git rev-parse --show-toplevel || die "Could not get GITDIR")" + +DATE="$(date '+%Y%m%d-%H%M%S')" + +BUILDLOG="$(mktemp ./.build.log.XXXXXXX)" +chmod +r "$BUILDLOG" + +exec > >(tee "$BUILDLOG") +exec 2>&1 + +UUID=`uuidgen` +RELEASE_VERSION="${RELEASE_VERSION:-$(git rev-list HEAD | wc -l)}" +SNAPSHOT="${SNAPSHOT:-%{nil\}}" +VERSION="${VERSION:-$(get_version || die "Could not read $VERSION")}" +COMMIT_FULL="${COMMIT_FULL:-$(git rev-parse --verify HEAD || die "Error reading HEAD revision")}" +COMMIT="${COMMIT:-$(printf '%s' "$COMMIT_FULL" | sed 's/^\(.\{10\}\).*/\1/' || die "Error reading HEAD revision")}" +BCOND_DEFAULT_DEBUG="${BCOND_DEFAULT_DEBUG:-0}" +BCOND_DEFAULT_TEST="${BCOND_DEFAULT_TEST:-0}" +BCOND_DEFAULT_LTO="${BCOND_DEFAULT_LTO}" +USERNAME="${USERNAME:-"$(git config user.name) <$(git config user.email)>"}" +SPECFILE="$(abs_path "$SPECFILE" "$SCRIPTDIR/NetworkManager.spec")" || die "invalid \$SPECFILE argument" +SOURCE_FROM_GIT="$(coerce_bool "$SOURCE_FROM_GIT" "")" +SOURCE="$(abs_path "$SOURCE")" || die "invalid \$SOURCE argument" +DO_RELEASE="$(coerce_bool "$DO_RELEASE" "0")" +SIGN_SOURCE="$(coerce_bool "$SIGN_SOURCE" "$DO_RELEASE")" +if [ -n "$SOURCE" ]; then + [[ "$SOURCE_FROM_GIT" == 1 ]] && die "Cannot set both \$SOURCE and \$SOURCE_FROM_GIT=1" + SOURCE_FROM_GIT=0 +elif [[ "$SOURCE_FROM_GIT" != "1" ]]; then + SOURCE="$(ls -1 "$GITDIR/NetworkManager-${VERSION}.tar."* 2>/dev/null | head -n1)" + if [[ -z "$SOURCE" ]]; then + [[ "$SOURCE_FROM_GIT" == "0" ]] && die "Either set \$SOURCE or set \$SOURCE_FROM_GIT=1" + SOURCE_FROM_GIT=1 + else + SOURCE_FROM_GIT=0 + fi +fi + +SOURCE_NETWORKMANAGER_CONF="$(abs_path "$SOURCE_NETWORKMANAGER_CONF" "$SCRIPTDIR/NetworkManager.conf")" || die "invalid \$SOURCE_NETWORKMANAGER_CONF argument" +SOURCE_CONFIG_SERVER="$(abs_path "$SOURCE_CONFIG_SERVER" "$SCRIPTDIR/00-server.conf")" || die "invalid \$SOURCE_CONFIG_SERVER argument" +SOURCE_CONFIG_CONNECTIVITY_FEDORA="$(abs_path "$SOURCE_CONFIG_CONNECTIVITY_FEDORA" "$SCRIPTDIR/20-connectivity-fedora.conf")" || die "invalid \$SOURCE_CONFIG_CONNECTIVITY_FEDORA argument" +SOURCE_CONFIG_CONNECTIVITY_REDHAT="$(abs_path "$SOURCE_CONFIG_CONNECTIVITY_REDHAT" "$SCRIPTDIR/20-connectivity-redhat.conf")" || die "invalid \$SOURCE_CONFIG_CONNECTIVITY_REDHAT argument" +SOURCE_CONFIG_WIFI_MAC_ADDR="$(abs_path "$SOURCE_CONFIG_WIFI_MAC_ADDR" "$SCRIPTDIR/22-wifi-mac-addr.conf")" || die "invalid \$SOURCE_CONFIG_WIFI_MAC_ADDR argument" +SOURCE_SYSCTL_RP_FILTER_REDHAT="$(abs_path "$SOURCE_SYSCTL_RP_FILTER_REDHAT" "$SCRIPTDIR/70-nm-connectivity.conf")" || die "invalid \$SOURCE_SYSCTL_RP_FILTER_REDHAT argument" +SOURCE_README_IFCFG_FILES="$(abs_path "$SOURCE_README_IFCFG_FILES" "$SCRIPTDIR/readme-ifcfg-rh.txt")" || die "invalid \$SOURCE_README_IFCFG_FILES argument" +SOURCE_README_IFCFG_MIGRATED="$(abs_path "$SOURCE_README_IFCFG_MIGRATED" "$SCRIPTDIR/readme-ifcfg-rh-migrated.txt")" || die "invalid \$SOURCE_README_IFCFG_MIGRATED argument" + +TEMP="$(mktemp -d "$SCRIPTDIR/NetworkManager.$DATE.XXXXXX")" +TEMPBASE="$(basename "$TEMP")" + +if [[ "$SOURCE_FROM_GIT" == "1" ]]; then + mkdir -p "$TEMP/SOURCES" + SOURCE="$TEMP/SOURCES/NetworkManager-${VERSION}.tar.xz" + (cd "$GITDIR" && git archive --prefix="NetworkManager-$VERSION"/ "$COMMIT_FULL") | xz > "$SOURCE" +fi + +LOG "VERSION=$VERSION" +LOG "RELEASE_VERSION=$RELEASE_VERSION" +LOG "SNAPSHOT=$SNAPSHOT" +LOG "COMMIT_FULL=$COMMIT_FULL" +LOG "COMMIT=$COMMIT" +LOG "USERNAME=$USERNAME" +LOG "SPECFILE=$SPECFILE" +LOG "SOURCE=$SOURCE" +LOG "SIGN_SOURCE=$SIGN_SOURCE" +LOG "DO_RELEASE=$DO_RELEASE" +LOG "SOURCE_FROM_GIT=$SOURCE_FROM_GIT" +LOG "SOURCE_NETWORKMANAGER_CONF=$SOURCE_NETWORKMANAGER_CONF" +LOG "SOURCE_CONFIG_SERVER=$SOURCE_CONFIG_SERVER" +LOG "SOURCE_CONFIG_CONNECTIVITY_FEDORA=$SOURCE_CONFIG_CONNECTIVITY_FEDORA" +LOG "SOURCE_CONFIG_CONNECTIVITY_REDHAT=$SOURCE_CONFIG_CONNECTIVITY_REDHAT" +LOG "SOURCE_SYSCTL_RP_FILTER_REDHAT=$SOURCE_SYSCTL_RP_FILTER_REDHAT" +LOG "SOURCE_README_IFCFG_FILES=$SOURCE_README_IFCFG_FILES" +LOG "SOURCE_README_IFCFG_MIGRATED=$SOURCE_README_IFCFG_MIGRATED" +LOG "BUILDTYPE=$BUILDTYPE" +LOG "NM_RPMBUILD_ARGS=$NM_RPMBUILD_ARGS" +LOG "BCOND_DEFAULT_DEBUG=$BCOND_DEFAULT_DEBUG" +LOG "BCOND_DEFAULT_LTO=$BCOND_DEFAULT_LTO" +LOG "BCOND_DEFAULT_TEST=$BCOND_DEFAULT_TEST" +LOG "" +LOG "UUID=$UUID" +LOG "BASEDIR=$TEMP" + +in_set "$BCOND_DEFAULT_DEBUG" 0 1 || die "Invalid value for \$BCOND_DEFAULT_DEBUG: \"$BCOND_DEFAULT_DEBUG\"" +in_set "$BCOND_DEFAULT_LTO" '' 0 1 || die "Invalid value for \$BCOND_DEFAULT_LTO: \"$BCOND_DEFAULT_LTO\"" +in_set "$BCOND_DEFAULT_TEST" 0 1 || die "Invalid value for \$BCOND_DEFAULT_TEST: \"$BCOND_DEFAULT_TEST\"" + +ln -snf "$TEMPBASE" ./latest0 +ln "$BUILDLOG" "$TEMPBASE/build.log" +rm -f "$BUILDLOG" + +TEMPSPEC="$TEMP/SPECS/NetworkManager.spec" +mkdir -p "$TEMP/SOURCES/" "$TEMP/SPECS/" || die "error creating SPECS directory" + +if [[ "$(dirname "$SOURCE")" != "$TEMP/SOURCES" ]]; then + cp "$SOURCE" "$TEMP/SOURCES/" || die "Could not copy source $SOURCE to $TEMP/SOURCES" +fi +cp "$SOURCE_NETWORKMANAGER_CONF" "$TEMP/SOURCES/NetworkManager.conf" || die "Could not copy source $SOURCE_NETWORKMANAGER_CONF to $TEMP/SOURCES" +cp "$SOURCE_CONFIG_SERVER" "$TEMP/SOURCES/00-server.conf" || die "Could not copy source $SOURCE_CONFIG_SERVER to $TEMP/SOURCES" +cp "$SOURCE_CONFIG_CONNECTIVITY_FEDORA" "$TEMP/SOURCES/20-connectivity-fedora.conf" || die "Could not copy source $SOURCE_CONFIG_CONNECTIVITY_FEDORA to $TEMP/SOURCES" +cp "$SOURCE_CONFIG_CONNECTIVITY_REDHAT" "$TEMP/SOURCES/20-connectivity-redhat.conf" || die "Could not copy source $SOURCE_CONFIG_CONNECTIVITY_REDHAT to $TEMP/SOURCES" +cp "$SOURCE_CONFIG_WIFI_MAC_ADDR" "$TEMP/SOURCES/22-wifi-mac-addr.conf" || die "Could not copy source $SOURCE_CONFIG_WIFI_MAC_ADDR to $TEMP/SOURCES" +cp "$SOURCE_SYSCTL_RP_FILTER_REDHAT" "$TEMP/SOURCES/70-nm-connectivity.conf" || die "Could not copy source $SOURCE_SYSCTL_RP_FILTER_REDHAT to $TEMP/SOURCES" +cp "$SOURCE_README_IFCFG_FILES" "$TEMP/SOURCES/readme-ifcfg-rh.txt" || die "Could not copy source $SOURCE_README_IFCFG_FILES to $TEMP/SOURCES" +cp "$SOURCE_README_IFCFG_MIGRATED" "$TEMP/SOURCES/readme-ifcfg-rh-migrated.txt" || die "Could not copy source $SOURCE_README_IFCFG_MIGRATED to $TEMP/SOURCES" + +write_changelog + +sed -e "s/__VERSION__/$VERSION/g" \ + -e "s/__RELEASE_VERSION__/$RELEASE_VERSION/g" \ + -e "s/__SNAPSHOT__/$SNAPSHOT/g" \ + -e "s/__COMMIT__/$COMMIT/g" \ + -e "s/__COMMIT_FULL__/$COMMIT_FULL/g" \ + -e "s/__SNAPSHOT__/$SNAPSHOT/g" \ + -e "s/__SOURCE1__/$(basename "$SOURCE")/g" \ + -e "s/__BCOND_DEFAULT_DEBUG__/$BCOND_DEFAULT_DEBUG/g" \ + -e "s/__BCOND_DEFAULT_LTO__/${BCOND_DEFAULT_LTO:-"%{nil}"}/g" \ + -e "s/__BCOND_DEFAULT_TEST__/$BCOND_DEFAULT_TEST/g" \ + "$SPECFILE" | +sed -e "/^__CHANGELOG__$/ \ + { + r $TEMPBASE/SOURCES/CHANGELOG + d + }" > "$TEMPSPEC" || die "Error reading spec file" + +case "$BUILDTYPE" in + "SRPM") + RPM_BUILD_OPTION=-bs + ;; + *) + RPM_BUILD_OPTION=-ba + ;; +esac + +rpmbuild --define "_topdir $TEMP" $RPM_BUILD_OPTION "$TEMPSPEC" $NM_RPMBUILD_ARGS || die "ERROR: rpmbuild FAILED" + +LS_EXTRA=() + +if [ "$SIGN_SOURCE" = 1 ]; then + SIGNKEY="$(git config --get user.signingkey)" + if [ "$SIGNKEY" != "" ]; then + SIGNKEY="--local-user $(printf '%q' "$SIGNKEY")" + fi + gpg $SIGNKEY --output "$SOURCE.sig" --armor --detach-sig "$SOURCE" || die "ERROR: failure to sign $SOURCE" + LS_EXTRA+=("$SOURCE.sig") +fi + +ln -snf "$TEMPBASE" ./latest +TEMP_LATEST="$(readlink -f .)"/latest + +LOG +LOG +LOG "Finished with success." +LOG +LOG "See \"$TEMP_LATEST/\" which symlinks to \"$TEMPBASE\"" +LOG +LOG "Result:" +ls -dla \ + "$TEMP_LATEST" \ + "$SOURCE" \ + "${LS_EXTRA[@]}" \ + "$(dirname "$TEMP_LATEST")/$TEMPBASE/" \ + "$TEMP_LATEST"/RPMS/*/ \ + "$TEMP_LATEST"/RPMS/*/*.rpm \ + "$TEMP_LATEST"/SRPMS/ \ + "$TEMP_LATEST"/SRPMS/*.rpm \ + 2>/dev/null | sed 's/^/ /' +LOG +if [[ "$BUILDTYPE" == "SRPM" ]]; then + LOG sudo $(command -v dnf &>/dev/null && echo dnf builddep || echo yum-builddep) $TEMP_LATEST/SRPMS/*.src.rpm + LOG +else + LOG "sudo $(command -v dnf &>/dev/null && echo dnf || echo yum) install '$TEMP_LATEST/RPMS'/*/*.rpm" + LOG +fi + +if [[ "$DO_RELEASE" == 1 ]]; then + LOG "RELEASE \"$SOURCE\" :" + for c in md5 sha1 sha256 sha512; do + LOG "$(printf '%8s: %s' "$c" $("${c}sum" "$SOURCE" | sed 's/ .*//'))" + done + LOG +fi diff --git a/contrib/fedora/rpm/build_clean.sh b/contrib/fedora/rpm/build_clean.sh new file mode 100755 index 00000000..d06d4341 --- /dev/null +++ b/contrib/fedora/rpm/build_clean.sh @@ -0,0 +1,303 @@ +#!/bin/bash + + +die() { + echo "$*" >&2 + exit 1 +} + +usage() { + echo "USAGE: $0 [-h|--help|-?|help] [-f|--force] [-c|--clean] [-S|--srpm] [-g|--git] [-Q|--quick] [-N|--no-dist] [[-w|--with OPTION] ...] [[-W|--without OPTION] ...]" + echo + echo "Does all the steps from a clean git working directory to an RPM of NetworkManager" + echo + echo "This is also the preferred way to create a distribution tarball for release:" + echo " $ $0 -r" + echo + echo "Options:" + echo " -f|--force: force build, even if working directory is not clean and has local modifications" + echo " -c|--clean: run \`git-clean -fdx :/\` before build" + echo " -S|--srpm: only build the SRPM" + echo " -g|--git: create tarball from current git HEAD (skips make dist)" + echo " -Q|--quick: only create the distribution tarball, without running checks" + echo " -N|--no-dist: skip creating the source tarball if you already did \`make dist\`" + echo " -m|--meson: (default) use meson to create the source tarball" + echo " -A|--autotools: use autotools to create the source tarball" + echo " -w|--with \$OPTION: pass --with \$OPTION to rpmbuild. For example --with debug" + echo " -W|--without \$OPTION: pass --without \$OPTION to rpmbuild. For example --without debug" + echo " -s|--snapshot TEXT: use TEXT as the snapshot version for the new package (overwrites \$NM_BUILD_SNAPSHOT environment)" + echo " -r|--release: built a release tarball (this option must be alone)" + echo " --default-for-debug \$OPTION: set the default for "debug" option in the generated spec file" + echo " --default-for-lto \$OPTION: set the default for "lto" option in the generated spec file" + echo " --default-for-test \$OPTION: set the default for "test" option in the generated spec file" +} + +in_set() { + local v="$1" + shift + for v2; do + test "$v" = "$v2" && return 0 + done + return 1 +} + +ORIGDIR="$(readlink -f "$PWD")" +SCRIPTDIR="$(dirname "$(readlink -f "$0")")" +GITDIR="$(cd "$SCRIPTDIR" && git rev-parse --show-toplevel || die "Could not get GITDIR")" + + +[[ -x "$SCRIPTDIR"/build.sh ]] || die "could not find \"$SCRIPTDIR/build.sh\"" + +cd "$GITDIR" || die "could not change to $GITDIR" + +IGNORE_DIRTY=0 +GIT_CLEAN=0 +QUICK=0 +NO_DIST=0 +WITH_LIST=() +SOURCE_FROM_GIT=0 +SNAPSHOT="$NM_BUILD_SNAPSHOT" +DO_RELEASE=0 +unset BCOND_DEFAULT_DEBUG +unset BCOND_DEFAULT_LTO +unset BCOND_DEFAULT_TEST + +ADD_WITH_TEST=1 + +NARGS=$# + +while [[ $# -gt 0 ]]; do + A="$1" + shift + case "$A" in + -h|--help|-\?|help) + usage + exit 0 + ;; + -f|--force) + IGNORE_DIRTY=1 + ;; + -r|--release) + [[ $NARGS -eq 1 ]] || die "--release option must be alone" + export NMTST_CHECK_GTK_DOC=1 + BUILDTYPE=SRPM + DO_RELEASE=1 + ;; + -c|--clean) + GIT_CLEAN=1 + ;; + -S|--srpm) + BUILDTYPE=SRPM + ;; + -s|--snapshot) + [[ $# -gt 0 ]] || die "Missing argument to $A" + SNAPSHOT="$1" + shift + ;; + -g|--git) + NO_DIST=1 + IGNORE_DIRTY=1 + SOURCE_FROM_GIT=1 + ;; + -m|--meson) + [ "$USE_AUTOTOOLS" = 1 ] && die "conflicting argument: $A when building with autotools is requested"; + USE_MESON=1 + ;; + -A|--autotools) + [ "$USE_MESON" = 1 ] && die "conflicting argument: $A when building with meson is explicitly requested"; + USE_AUTOTOOLS=1 + ;; + -Q|--quick) + QUICK=1 + ;; + -N|--no-dist) + NO_DIST=1 + IGNORE_DIRTY=1 + SOURCE_FROM_GIT=0 + ;; + -w|--with) + [[ $# -gt 0 ]] || die "Missing argument to $A" + WITH_LIST=("${WITH_LIST[@]}" "--with" "$1") + case "$1" in + debug) + [[ -z ${BCOND_DEFAULT_DEBUG+.} ]] && BCOND_DEFAULT_DEBUG=1 + ;; + lto) + [[ -z ${BCOND_DEFAULT_LTO+.} ]] && BCOND_DEFAULT_LTO=1 + ;; + test) + ADD_WITH_TEST=0 + [[ -z ${BCOND_DEFAULT_TEST+.} ]] && BCOND_DEFAULT_TEST=1 + ;; + esac + shift + ;; + -W|--without) + [[ $# -gt 0 ]] || die "Missing argument to $A" + WITH_LIST=("${WITH_LIST[@]}" "--without" "$1") + case "$1" in + debug) + [[ -z ${BCOND_DEFAULT_DEBUG+.} ]] && BCOND_DEFAULT_DEBUG=0 + ;; + lto) + [[ -z ${BCOND_DEFAULT_LTO+.} ]] && BCOND_DEFAULT_LTO=0 + ;; + test) + ADD_WITH_TEST=0 + [[ -z ${BCOND_DEFAULT_TEST+.} ]] && BCOND_DEFAULT_TEST=0 + ;; + esac + shift + ;; + --no-auto-with-test) + # by default, the script adds "-w test" (unless the command line contains + # "-w test" or "-W test"). This flags allows to suppress that automatism. + # It's really only useful to test the spec file's internal default for the + # "test" option. Otherwise, you can always just explicitly select "-w test" + # or "-W test". + ADD_WITH_TEST=0 + ;; + --default-for-debug) + [[ $# -gt 0 ]] || die "Missing argument to $A" + in_set "$1" "" 0 1 || die "invalid argument $A \"$1\"" + BCOND_DEFAULT_DEBUG="$1" + shift + ;; + --default-for-lto) + [[ $# -gt 0 ]] || die "Missing argument to $A" + in_set "$1" "" 0 1 || die "invalid argument $A \"$1\"" + BCOND_DEFAULT_LTO="$1" + shift + ;; + --default-for-test) + [[ $# -gt 0 ]] || die "Missing argument to $A" + in_set "$1" "" 0 1 || die "invalid argument $A \"$1\"" + BCOND_DEFAULT_TEST="$1" + shift + ;; + *) + usage + die "Unexpected argument \"$A\"" + ;; + esac +done + +if [[ $GIT_CLEAN == 1 ]]; then + git clean -fdx :/ +fi + +if [[ $IGNORE_DIRTY != 1 ]]; then + # check for a clean working directory. + # We ignore the /contrib directory, because this is where the automation + # scripts and the build results will be. + if [[ "x$(LANG=C git clean -ndx | grep '^Would \(remove contrib/\|skip repository libgsystem/\).*$' -v)" != x ]]; then + die "The working directory is not clean. Refuse to run. Try \`$0 --force\`, \`$0 --clean\`, or \`git clean -e :/contrib -dx -n\`" + fi + if [[ "x$(git status --porcelain)" != x ]]; then + die "The working directory has local changes. Refuse to run. Try \`$0 --force\`" + fi +fi + +get_version_meson() { + meson introspect "$GITDIR/build" --projectinfo | jq -r .version +} + +if [[ $NO_DIST != 1 ]]; then + if [[ $USE_AUTOTOOLS != 1 ]]; then + meson setup "$GITDIR/build" \ + --prefix=/usr \ + --bindir=/usr/bin \ + --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --datadir=/usr/share \ + --includedir=/usr/include \ + --libdir=/usr/lib \ + --libexecdir=/usr/libexec \ + --localstatedir=/var \ + --sharedstatedir=/var/lib \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info \ + -Ddocs=true \ + -Dintrospection=true \ + -Difcfg_rh=true \ + -Difupdown=true \ + -Dconfig_logging_backend_default=syslog \ + -Dconfig_wifi_backend_default=wpa_supplicant \ + -Dlibaudit=yes-disabled-by-default \ + -Dpolkit=true \ + -Dnm_cloud_setup=true \ + -Dconfig_dhcp_default=internal \ + -Dconfig_dns_rc_manager_default=auto \ + -Diptables=/usr/sbin/iptables \ + -Dnft=/usr/bin/nft \ + || die "Error meson setup" + + VERSION="${VERSION:-$(get_version_meson || die "Could not read $VERSION")}" + if [[ $QUICK == 1 ]]; then + meson dist --allow-dirty -C "$GITDIR/build/" --no-tests || die "Error meson dist" + else + meson dist --allow-dirty -C "$GITDIR/build/" || die "Error meson dist with tests" + fi + export SOURCE="$(ls -1 "$GITDIR/build/meson-dist/NetworkManager-${VERSION}.tar.xz" 2>/dev/null | head -n1)" + else + ./autogen.sh \ + --with-runstatedir=/run \ + --program-prefix= \ + --prefix=/usr \ + --exec-prefix=/usr \ + --bindir=/usr/bin \ + --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --datadir=/usr/share \ + --includedir=/usr/include \ + --libdir=/usr/lib \ + --libexecdir=/usr/libexec \ + --localstatedir=/var \ + --sharedstatedir=/var/lib \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info \ + \ + --disable-dependency-tracking \ + --enable-gtk-doc \ + --enable-introspection \ + --enable-ifcfg-rh \ + --enable-ifupdown \ + --with-config-logging-backend-default=syslog \ + --with-config-wifi-backend-default=wpa_supplicant \ + --with-libaudit=yes-disabled-by-default \ + --enable-polkit=yes \ + --with-nm-cloud-setup=yes \ + --with-config-dhcp-default=internal \ + --with-config-dns-rc-manager-default=auto \ + \ + --with-iptables=/usr/sbin/iptables \ + --with-nft=/usr/sbin/nft \ + \ + || die "Error autogen.sh" + if [[ $QUICK == 1 ]]; then + make dist -j 7 || die "Error make dist" + else + make distcheck -j 7 || die "Error make distcheck" + fi + fi +fi + +if [[ "$ADD_WITH_TEST" == 1 ]]; then + WITH_LIST=("${WITH_LIST[@]}" "--with" "test") +fi + +if [[ "$USE_AUTOTOOLS" != 1 ]]; then + WITH_LIST=("${WITH_LIST[@]}" "--with" "meson") +fi + +export SOURCE_FROM_GIT +export BUILDTYPE +export NM_RPMBUILD_ARGS="${WITH_LIST[@]}" +export SNAPSHOT +export DO_RELEASE +export BCOND_DEFAULT_DEBUG="$BCOND_DEFAULT_DEBUG" +export BCOND_DEFAULT_LTO="$BCOND_DEFAULT_LTO" +export BCOND_DEFAULT_TEST="$BCOND_DEFAULT_TEST" + +"$SCRIPTDIR"/build.sh + diff --git a/contrib/fedora/rpm/configure-for-system.sh b/contrib/fedora/rpm/configure-for-system.sh new file mode 100755 index 00000000..acf5eb9c --- /dev/null +++ b/contrib/fedora/rpm/configure-for-system.sh @@ -0,0 +1,526 @@ +#!/bin/bash + +# Run configure/meson for NetworkManager in a way similar to how an RPM build does it. +# The effect is, that if you do `make install`, that it will overwrite the files that +# you'd usually get by installing the NetworkManager RPM. Also, it means you can afterwards +# systemctl restart NetworkManager. + +die() { + printf "%s\n" "$*" + exit 1 +} + +BASE_DIR="$(cd "$(dirname "$BASH_SOURCE")"; git rev-parse --show-toplevel)" + +cd "$BASE_DIR" || die "Cannot cd to base directory" + +vars() { + sed -e '1,/[P]VARS/!d' "$BASH_SOURCE" | sed -n 's/^'"$1"'_\([^=]*\)=.*/\1/p' +} + +vars_with_vals() { + echo "Variables:" + for v in $(vars P); do + printf " %s=%q\n" "$v" "$(eval "echo \"\$P_$v\"")" + done + echo "Directories:" + for v in $(vars D); do + printf " %s=%q\n" "$v" "$(eval "echo \"\$D_$v\"")" + done +} + +usage() { + echo "$ $0 [-m|--meson] [-a|--autotools] [-s|--show] [-B|--no-build] [-h|--help]" + echo "" + echo "Configure NetworkManager in a way that is similar to when building" + echo "RPMs of NetworkManager for Fedora/RHEL. The effect is that \`make install\`" + echo "will overwrite the files in /usr that you installed via the package management" + echo "systemd. Also, subsequent \`systemctl restart NetworkManager\` works." + echo "You don't want to do this on your real system, because it messes up your" + echo "installation" + echo + + vars_with_vals +} + +get_version() { + local major minor micro + local F="./configure.ac" + + vars="$(sed -n 's/^m4_define(\[nm_\(major\|minor\|micro\)_version\], *\[\([0-9]\+\)\]) *$/local \1='\''\2'\''/p' "$F" 2>/dev/null)" + eval "$vars" + + [[ -n "$major" && -n "$minor" && "$micro" ]] || return 1 + echo "$major.$minor.$micro" +} + +bool() { + case "$1" in + 1|y|Y|yes|Yes|YES|true|True|TRUE) + return 0 + ;; + 0|n|N|no|No|NO|false|False|FALSE) + return 1 + ;; + *) + local re='^[0-9]+$' + [[ $1 =~ $re ]] && test "$1" -gt 0 && return 0 + [ "$#" -le "1" ] && return 1 + shift + bool "$@" + return $? + ;; + esac +} + +bool_true() { + if bool "$@"; then + echo true + else + echo false + fi +} + +bool_not_true() { + if bool "$@"; then + echo false + else + echo true + fi +} + +bool_enable() { + if bool "$@"; then + echo enable + else + echo disable + fi +} + +bool_not_enable() { + if bool "$@"; then + echo disable + else + echo enable + fi +} + +bool_yes() { + if bool "$@"; then + echo yes + else + echo no + fi +} + +bool_not_yes() { + if bool "$@"; then + echo no + else + echo yes + fi +} + +args_enable() { + local cond="$1" + local a + shift + if bool "$cond" ; then + for a; do + printf "%q\n" "$a" + done + fi +} + +show_cmd() { + local a + local sep= + + for a; do + printf '%s%q' "$sep" "$a" + sep=' ' + done + printf '\n' +} + +SHOW_CMD= + +P_NOBUILD="${NOBUILD-0}" + +P_DEBUG="${DEBUG-1}" + +P_BUILD_TYPE="${BUILD_TYPE-}" +P_CFLAGS="${CFLAGS-}" +P_CC="${CC-$((! command -v gcc && command -v clang) &>/dev/null && echo clang || echo gcc)}" + +P_RHEL="${RHEL-}" +P_FEDORA="${FEDORA-}" + +P_CONFIG_PLUGINS_DEFAULT_IFCFG_RH="${CONFIG_PLUGINS_DEFAULT_IFCFG_RH-}" +P_CRYPTO="${CRYPTO-}" +P_DBUS_SYS_DIR="${DBUS_SYS_DIR-}" +P_DHCP_DEFAULT="${DHCP_DEFAULT-}" +P_DNS_RC_MANAGER_DEFAULT="${DNS_RC_MANAGER_DEFAULT-}" +P_EBPF_ENABLED="${EBPF_ENABLED-no}" +P_FIREWALLD_ZONE="${FIREWALLD_ZONE-}" +P_IWD="${IWD-}" +P_LOGGING_BACKEND_DEFAULT="${LOGGING_BACKEND_DEFAULT-}" +P_LTO="${LTO-0}" +P_MODEM_MANAGER_1="${MODEM_MANAGER_1-}" +P_TEST="${TEST-1}" +P_SILENT_RULES="${SILENT_RULES-1}" + +P_VERSION="${VERSION:-$(get_version)}" +P_RELEASE="${RELEASE:-$(git rev-list HEAD | wc -l).test}" + +P_REGEN_DOCS="${REGEN_DOCS-1}" +P_SANITIZER="${SANITIZER-0}" + +P_WIFI="${WIFI-1}" +P_WWAN="${WWAN-1}" +P_TEAM="${TEAM-1}" +P_BLUETOOTH="${BLUETOOTH-1}" +P_NMTUI="${NMTUI-1}" +P_NM_CLOUD_SETUP="${NM_CLOUD_SETUP-1}" +P_OVS="${OVS-1}" +P_PPP="${PPP-1}" + +P_PPP_VERSION="${PPP_VERSION-}" + +D_PREFIX="$(rpm --eval "%{_prefix}")" +D_BINDIR="$(rpm --eval "%{_bindir}")" +D_SBINDIR="$(rpm --eval "%{_sbindir}")" +D_LIBDIR="$(rpm --eval "%{_libdir}")" +D_LIBEXECDIR="$(rpm --eval "%{_libexecdir}")" +D_INCLUDEDIR="$(rpm --eval "%{_includedir}")" +D_DATADIR="$(rpm --eval "%{_datadir}")" +D_RUNDIR="$(rpm --eval "%{_rundir}")" +D_MANDIR="$(rpm --eval "%{_mandir}")" +D_INFODIR="$(rpm --eval "%{_infodir}")" +D_SYSCONFDIR="$(rpm --eval "%{_sysconfdir}")" +D_LOCALSTATEDIR="$(rpm --eval "%{_localstatedir}")" +D_SHAREDSTATEDIR="$(rpm --eval "%{_sharedstatedir}")" + +#PVARS + +if [ -z "$P_FEDORA" -a -z "$P_RHEL" ] ; then + x="$(grep -q "ID=fedora" /etc/os-release && sed -n 's/VERSION_ID=//p' /etc/os-release)" + if test "$x" -gt 0 ; then + P_FEDORA="$x" + P_RHEL=0 + else + x="$(grep -q "ID=fedora" /etc/os-release && sed -n 's/VERSION_ID=//p' /etc/os-release)" + if test "$x" -gt 0 ; then + P_FEDORA=0 + P_RHEL="$x" + fi + fi +fi +test -z "$P_FEDORA" && P_FEDORA=0 +test -z "$P_RHEL" && P_RHEL=0 + +test "$P_FEDORA" -gt 0 -o "$P_RHEL" -gt 0 || die "FEDORA/RHEL variables unset" + +if [ -z "$P_PPP_VERSION" ] ; then + P_PPP_VERSION="$(sed -n 's/^#define\s*VERSION\s*"\([^\s]*\)"$/\1/p' "$D_INCLUDEDIR/pppd/patchlevel.h" 2>/dev/null | grep . || echo bad)" +fi + +if [ -z "$P_CRYPTO" ] ; then + if [ "$P_FEDORA" -ge 29 -o "$P_RHEL" -ge 8 ] ; then + P_CRYPTO=gnutls + else + P_CRYPTO=nss + fi +fi + +if [ -z "$P_CONFIG_PLUGINS_DEFAULT_IFCFG_RH" ] ; then + if [ "$P_FEDORA" -ge 33 -o "$P_RHEL" -ge 9 ] ; then + P_CONFIG_PLUGINS_DEFAULT_IFCFG_RH=0 + else + P_CONFIG_PLUGINS_DEFAULT_IFCFG_RH=1 + fi +fi + + +if [ -z "$P_DBUS_SYS_DIR" ] ; then + if [ "$P_FEDORA" -ge 1 -o "$P_RHEL" -ge 8 ] ; then + P_DBUS_SYS_DIR="$D_DATADIR/dbus-1/system.d" + else + P_DBUS_SYS_DIR="$D_SYSCONFDIR/dbus-1/system.d" + fi +fi + +if [ -z "$P_DNS_RC_MANAGER_DEFAULT" ] ; then + if [ "$P_FEDORA" -ge 1 -o "$P_RHEL" -ge 9 ] ; then + P_DNS_RC_MANAGER_DEFAULT=auto + elif [ "$P_FEDORA" -ge 1 -o "$P_RHEL" -ge 8 ] ; then + P_DNS_RC_MANAGER_DEFAULT=symlink + else + P_DNS_RC_MANAGER_DEFAULT=file + fi +fi + +if [ -z "$P_LOGGING_BACKEND_DEFAULT" ] ; then + if [ "$P_FEDORA" -ge 1 -o "$P_RHEL" -ge 8 ] ; then + P_LOGGING_BACKEND_DEFAULT=journal + else + P_LOGGING_BACKEND_DEFAULT=syslog + fi +fi + +if [ -z "$P_DHCP_DEFAULT" ] ; then + if [ "$P_FEDORA" -ge 31 -o "$P_RHEL" -ge 8 ] ; then + P_DHCP_DEFAULT=internal + else + P_DHCP_DEFAULT=dhclient + fi +fi + +if [ -z "$P_FIREWALLD_ZONE" ] ; then + if [ "$P_FEDORA" -ge 32 -o "$P_RHEL" -ge 8 ] ; then + P_FIREWALLD_ZONE=1 + else + P_FIREWALLD_ZONE=0 + fi +fi + +if [ -z "$P_IWD" ] ; then + if [ "$P_RHEL" -ge 1 ] ; then + P_IWD=0 + else + P_IWD=1 + fi +fi + +if [ -z "$P_MODEM_MANAGER_1" ] ; then + if bool "$P_BLUETOOTH" || bool "$P_WWAN" ; then + P_MODEM_MANAGER_1=1 + else + P_MODEM_MANAGER_1=0 + fi +fi + +if bool "$P_DEBUG" ; then + P_CFLAGS="-g -Og -fexceptions${P_CFLAGS:+ }$P_CFLAGS" +else + P_CFLAGS="-g -O2 -fexceptions${P_CFLAGS:+ }$P_CFLAGS" +fi + +if [ -z "$P_BUILD_TYPE" ] ; then + if [ -d ./build -a ! -f ./configure ] ; then + P_BUILD_TYPE=meson + elif [ ! -d ./build -a -f ./configure ] ; then + P_BUILD_TYPE=autotools + else + P_BUILD_TYPE=autotools + fi +fi + +while [[ $# -gt 0 ]] ; do + A="$1" + shift + case "$A" in + --meson|-m) + P_BUILD_TYPE=meson + ;; + --autotools|-a) + P_BUILD_TYPE=autotools + ;; + -s|--show) + SHOW_CMD=show_cmd + ;; + -h|help|-help|--help) + usage + exit 0 + ;; + -B|--no-build) + P_NOBUILD=1 + ;; + *) + usage + exit 1 + ;; + esac +done + +vars_with_vals + +if [ "$P_BUILD_TYPE" == meson ] ; then + MESON_RECONFIGURE= + if test -d "./build/" ; then + MESON_RECONFIGURE="--reconfigure" + fi + + $SHOW_CMD \ + env \ + CC="$P_CC" \ + CFLAGS="$P_CFLAGS" \ + meson \ + --buildtype=plain \ + --prefix="$D_PREFIX" \ + --libdir="$D_LIBDIR" \ + --libexecdir="$D_LIBEXECDIR" \ + --bindir="$D_BINDIR" \ + --sbindir="$D_SBINDIR" \ + --includedir="$D_INCLUDEDIR" \ + --datadir="$D_DATADIR" \ + --mandir="$D_MANDIR" \ + --infodir="$D_INFODIR" \ + --localedir="$D_DATADIR"/locale \ + --sysconfdir="$D_SYSCONFDIR" \ + --localstatedir="$D_LOCALSTATEDIR" \ + --sharedstatedir="$D_SHAREDSTATEDIR" \ + --wrap-mode=nodownload \ + --auto-features=enabled \ + \ + build \ + \ + $MESON_RECONFIGURE \ + \ + -Db_ndebug=false \ + --warnlevel 2 \ + $(args_enable "$P_TEST" --werror) \ + -Dnft="${D_SBINDIR}/nft" \ + -Diptables="${D_SBINDIR}/iptables" \ + -Ddhclient="${D_SBINDIR}/dhclient" \ + -Ddhcpcanon=no \ + -Ddhcpcd=no \ + -Dconfig_dhcp_default="$P_DHCP_DEFAULT" \ + "-Dcrypto=$P_CRYPTO" \ + $(args_enable "$P_DEBUG" -Dmore_logging=true -Dmore_asserts=10000) \ + $(args_enable "$(bool_not_true "$P_DEBUG")" -Dmore_logging=false -Dmore_asserts=0 ) \ + -Dld_gc=true \ + -Db_lto="$(bool_true "$P_LTO")" \ + -Dlibaudit=yes-disabled-by-default \ + -Dmodem_manager="$(bool_true "$P_MODEM_MANAGER_1")" \ + $(args_enable "$P_WIFI" -Dwifi=true -Dwext="$(bool_true "$P_FEDORA")") \ + $(args_enable "$(bool_not_true "$P_WIFI")" -Dwifi=false ) \ + -Diwd="$(bool_true "$P_IWD")" \ + -Dbluez5_dun="$(bool_true "$P_BLUETOOTH")" \ + -Dnmtui="$(bool_true "$P_NMTUI")" \ + -Dnm_cloud_setup="$(bool_true "$P_NM_CLOUD_SETUP")" \ + -Dvapi=true \ + -Dintrospection=true \ + -Ddocs="$(bool_true "$P_REGEN_DOCS")" \ + -Dteamdctl="$(bool_true "$P_TEAM")" \ + -Dovs="$(bool_true "$P_OVS")" \ + -Dselinux=true \ + -Dpolkit=true \ + -Dconfig_auth_polkit_default=true \ + -Dmodify_system=true \ + -Dconcheck=true \ + -Dlibpsl="$(bool_true "$P_FEDORA")" \ + -Debpf="$(bool_true "$P_EBPF_ENABLED")" \ + -Dsession_tracking=systemd \ + -Dsuspend_resume=systemd \ + -Dsystemdsystemunitdir=/usr/lib/systemd/system \ + -Dsystem_ca_path=/etc/pki/tls/cert.pem \ + -Ddbus_conf_dir="$P_DBUS_SYS_DIR" \ + -Dtests=yes \ + -Dvalgrind=no \ + -Difcfg_rh=true \ + -Difupdown=false \ + $(args_enable "$P_PPP" -Dppp=true -Dpppd="$D_SBINDIR/pppd" -Dpppd_plugin_dir="$D_LIBDIR/pppd/$P_PPP_VERSION") \ + $(args_enable "$(bool_not_true "$P_PPP")" -Dppp=false ) \ + -Dfirewalld_zone="$(bool_true "$P_FIREWALLD_ZONE}")" \ + -Ddist_version="$P_VERSION-$P_RELEASE" \ + $(args_enable "$P_CONFIG_PLUGINS_DEFAULT_IFCFG_RH" -Dconfig_plugins_default=ifcfg-rh) \ + -Dresolvconf=no \ + -Dnetconfig=no \ + -Dconfig_dns_rc_manager_default="$P_DNS_RC_MANAGER_DEFAULT" \ + -Dconfig_logging_backend_default="$P_LOGGING_BACKEND_DEFAULT" \ + ; +else + if ! test -x ./configure ; then + if [ -z "$SHOW_CMD" ]; then + NOCONFIGURE=yes ./autogen.sh + fi + fi + $SHOW_CMD \ + ./configure \ + --build=x86_64-redhat-linux-gnu \ + --host=x86_64-redhat-linux-gnu \ + --program-prefix= \ + --prefix="$D_PREFIX" \ + --exec-prefix=/usr \ + --bindir="$D_BINDIR" \ + --sbindir="$D_SBINDIR" \ + --sysconfdir="$D_SYSCONFDIR" \ + --datadir="$D_DATADIR" \ + --includedir="$D_INCLUDEDIR" \ + --libdir="$D_LIBDIR" \ + --libexecdir="$D_LIBEXECDIR" \ + --localstatedir="$D_LOCALSTATEDIR" \ + --sharedstatedir="$D_SHAREDSTATEDIR" \ + --mandir="$D_MANDIR" \ + --infodir="$D_INFODIR" \ + \ + CC="$P_CC" \ + CFLAGS="$P_CFLAGS" \ + \ + --enable-dependency-tracking=yes \ + \ + --with-runstatedir="$D_RUNDIR" \ + --enable-silent-rules="$(bool_yes "$P_SILENT_RULES")" \ + --enable-static=no \ + --with-nft="${D_SBINDIR}/nft" \ + --with-iptables="${D_SBINDIR}/iptables" \ + --with-dhclient="${D_SBINDIR}/dhclient" \ + --with-dhcpcd=no \ + --with-dhcpcanon=no \ + --with-config-dhcp-default="$P_DHCP_DEFAULT" \ + --with-crypto="$P_CRYPTO" \ + $(args_enable "$P_SANITIZER" --with-address-sanitizer=exec --enable-undefined-sanitizer="$( (bool "$P_FEDORA" || test "$P_RHEL" -ge 8) && echo yes || echo no)" ) \ + $(args_enable "$(bool_not_true "$P_SANITIZER")" --with-address-sanitizer=no --enable-undefined-sanitizer=no ) \ + $(args_enable "$P_DEBUG" --enable-more-logging=yes --with-more-asserts=10000) \ + $(args_enable "$(bool_not_true "$P_DEBUG")" --enable-more-logging=no --with-more-asserts=0 ) \ + --enable-ld-gc=yes \ + --enable-lto="$(bool_yes "$P_LTO")" \ + --with-libaudit=yes-disabled-by-default \ + --with-modem-manager-1="$(bool_yes "$P_MODEM_MANAGER_1")" \ + $(args_enable "$P_WIFI" --enable-wifi=yes --with-wext="$(bool_yes "$P_FEDORA")") \ + $(args_enable "$(bool_not_true "$P_WIFI")" --enable-wifi=no ) \ + --with-iwd="$(bool_yes "$P_IWD")" \ + --enable-bluez5-dun="$(bool_yes "$P_BLUETOOTH")" \ + --with-nmtui="$(bool_yes "$P_NMTUI")" \ + --with-nm-cloud-setup="$(bool_yes "$P_NM_CLOUD_SETUP")" \ + --enable-vala=yes \ + --enable-introspection=yes \ + --enable-gtk-doc="$(bool_yes "$P_REGEN_DOCS")" \ + --enable-teamdctl="$(bool_yes "$P_TEAM")" \ + --enable-ovs="$(bool_yes "$P_OVS")" \ + --with-selinux=yes \ + --enable-polkit=yes \ + --enable-modify-system=yes \ + --enable-concheck=yes \ + --with-libpsl="$(bool_yes "$P_FEDORA")" \ + --with-ebpf="$(bool_yes "$P_EBPF_ENABLED")" \ + --with-session-tracking=systemd \ + --with-suspend-resume=systemd \ + --with-systemdsystemunitdir=/usr/lib/systemd/system \ + --with-system-ca-path=/etc/pki/tls/cert.pem \ + --with-dbus-sys-dir="$P_DBUS_SYS_DIR" \ + --with-tests=yes \ + --enable-more-warnings="$(bool "$P_TEST" && echo error || echo yes)" \ + --with-valgrind=no \ + --enable-ifcfg-rh=yes \ + --enable-ifupdown=no \ + $(args_enable "$P_PPP" --enable-ppp=yes --with-pppd="$D_SBINDIR/pppd" --with-pppd-plugin-dir="$D_LIBDIR/pppd/$P_PPP_VERSION") \ + $(args_enable "$(bool_not_true "$P_PPP")" --enable-ppp=no ) \ + --enable-firewalld-zone="$(bool_yes "$P_FIREWALLD_ZONE")" \ + --with-dist-version="$P_VERSION-$P_RELEASE" \ + $(args_enable "$P_CONFIG_PLUGINS_DEFAULT_IFCFG_RH" --with-config-plugins-default=ifcfg-rh) \ + --with-resolvconf=no \ + --with-netconfig=no \ + --with-config-dns-rc-manager-default="$P_DNS_RC_MANAGER_DEFAULT" \ + --with-config-logging-backend-default="$P_LOGGING_BACKEND_DEFAULT" \ + ; +fi + +if ! bool "$P_NOBUILD" ; then + if [ "$P_BUILD_TYPE" == meson ] ; then + $SHOW_CMD ninja -C build + else + $SHOW_CMD make -j 10 + fi +fi diff --git a/contrib/fedora/rpm/mockbuild.sh b/contrib/fedora/rpm/mockbuild.sh new file mode 100755 index 00000000..785aa254 --- /dev/null +++ b/contrib/fedora/rpm/mockbuild.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# +# mockbuild.sh +# +# Generate SRPM from git tree and rebuild it using mock. + +SCRIPTDIR="$(dirname "$(readlink -f "$0")")" +FEDORAVER=$(sed -E 's/.*([0-9]{2}).*/\1/g' /etc/fedora-release) +ARCH=$(uname -m) +SRPM=${SCRIPTDIR}/latest/SRPMS/NetworkManager*.src.rpm + +alias mock="mock -r fedora-${FEDORAVER}-${ARCH}" + +# Generate SRPM +${SCRIPTDIR}/build_clean.sh --srpm --git + +# Rebuild SRPM +mock --rebuild ${SRPM} + +exit diff --git a/contrib/fedora/rpm/readme-ifcfg-rh-migrated.txt b/contrib/fedora/rpm/readme-ifcfg-rh-migrated.txt new file mode 100644 index 00000000..aabbcc86 --- /dev/null +++ b/contrib/fedora/rpm/readme-ifcfg-rh-migrated.txt @@ -0,0 +1,84 @@ +NetworkManager was built to automatically migrate connection profiles in +this directory to equivalent ones in keyfile format in directory +/etc/NetworkManager/system-connections. + +You can check whether the migration is enabled via: + + $ NetworkManager --print-config | grep migrate-ifcfg-rh + +In case it is enabled, all files in this directory are migrated at startup. + +To inspect where your connection files are currently stored use: + + $ nmcli -f name,uuid,filename connection + +Background +========== + +The ifcfg format is deprecated and will be removed in future releases. For +more information see: + +https://lists.freedesktop.org/archives/networkmanager/2023-May/000103.html + +Connection profiles in keyfile format have many benefits. For example, this +format is INI file-based and can easily be parsed and generated. + +Each section in NetworkManager keyfiles corresponds to a NetworkManager +setting name as described in the nm-settings(5) and nm-settings-keyfile(5) +man pages. Each key-value pair in a section is one of the properties listed +in the settings specification of the man page. + +How to keep using ifcfg +======================= + +If you want to keep using connection profiles in ifcfg format, you need to: + + - disable the automatic migration to keyfile by setting + "migrate-ifcfg-rh=false" in the [main] section of NetworkManager + configuration; + + - optionally, set "plugins=ifcfg-rh" in the [main] section of + NetworkManager configuration so that new profiles are created in ifcfg + format. + +At this point, you can migrate all your files back via + + nmcli connection migrate --plugin ifcfg-rh + +Or, if you prefer to migrate only specific connections: + + nmcli connection migrate --plugin ifcfg-rh <profile_name|UUID> + +Note that some connection types are not supported by the ifcfg plugin. + +Interface renaming +================== + +Connection profiles stored in ifcfg-rh format support the renaming of +interfaces via udev. This is done via a helper tool +/usr/lib/udev/rename_device that is invoked by udev to parse the files +in /etc/sysconfig/network-scripts; when the HWADDR and DEVICE +variables are set, the interface that matches the MAC address in +HWADDR is renamed to the name specified in DEVICE. + +Connections in keyfile format don't provide the same integration with +udev. The renaming of interfaces must be configured directly in udev, +for example by creating a file: + + /etc/systemd/network/70-rename.link + +with content: + + [Match] + MACAddress=00:11:22:33:44:56 + + [Link] + Name=ethernet1 + +Alternatively, a udev rule can also be used, such as: + + /etc/udev/rules.d/70-interface-names.rules + +with content: + + SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="00:11:22:33:44:56",ATTR{type}=="1",NAME="ethernet1" diff --git a/contrib/fedora/rpm/readme-ifcfg-rh.txt b/contrib/fedora/rpm/readme-ifcfg-rh.txt new file mode 100644 index 00000000..56c373d4 --- /dev/null +++ b/contrib/fedora/rpm/readme-ifcfg-rh.txt @@ -0,0 +1,63 @@ +NetworkManager stores new network profiles in keyfile format in the +/etc/NetworkManager/system-connections/ directory. + +Previously, NetworkManager stored network profiles in ifcfg format +in this directory (/etc/sysconfig/network-scripts/). However, the ifcfg +format is deprecated. By default, NetworkManager no longer creates +new profiles in this format. + +Connection profiles in keyfile format have many benefits. For example, +this format is INI file-based and can easily be parsed and generated. + +Each section in NetworkManager keyfiles corresponds to a NetworkManager +setting name as described in the nm-settings(5) and nm-settings-keyfile(5) +man pages. Each key-value-pair in a section is one of the properties +listed in the settings specification of the man page. + +If you still use network profiles in ifcfg format, consider migrating +them to keyfile format. To migrate all profiles at once, enter: + +# nmcli connection migrate + +This command migrates all profiles from ifcfg format to keyfile +format and stores them in /etc/NetworkManager/system-connections/. + +Alternatively, to migrate only a specific profile, enter: + +# nmcli connection migrate <profile_name|UUID|D-Bus_path> + +For further details, see: +* nm-settings-keyfile(5) +* nmcli(1) + +Interface renaming +================== + +Connection profiles stored in ifcfg-rh format support the renaming of +interfaces via udev. This is done via a helper tool +/usr/lib/udev/rename_device that is invoked by udev to parse the files +in /etc/sysconfig/network-scripts; when the HWADDR and DEVICE +variables are set, the interface that matches the MAC address in +HWADDR is renamed to the name specified in DEVICE. + +Connections in keyfile format don't provide the same integration with +udev. The renaming of interfaces must be configured directly in udev, +for example by creating a file: + + /etc/systemd/network/70-rename.link + +with content: + + [Match] + MACAddress=00:11:22:33:44:56 + + [Link] + Name=ethernet1 + +Alternatively, a udev rule can also be used, such as: + + /etc/udev/rules.d/70-interface-names.rules + +with content: + + SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="00:11:22:33:44:56",ATTR{type}=="1",NAME="ethernet1" diff --git a/contrib/fedora/rpm/release.sh b/contrib/fedora/rpm/release.sh new file mode 100755 index 00000000..809810f0 --- /dev/null +++ b/contrib/fedora/rpm/release.sh @@ -0,0 +1,604 @@ +#!/bin/bash + +# Script for doing NetworkManager releases. +# +# Run with --help for usage. +# +# There are 6 modes: +# +# - "devel" : on main branch to tag a devel release (e.g. "1.25.2-dev"). +# - "rc1" : the first release candidate on "main" branch which branches off +# a new "nm-1-X" branch (e.g. tag "1.26-rc1" (1.25.90) and branch +# off "nm-1-26"). On main this also bumps the version number +# and creates a new devel release (e.g. "1.27.0-dev"). +# - "rc" : further release candidates on RC branch (e.g. from "nm-1-26" branch +# tag "1.26-rc2" with version number 1.25.91). +# - "major" : on stable branch do a major release (e.g. on "nm-1-26" branch +# release "1.26.0", followed by "1.26.1-dev"). +# You should do a "major-post" release right a "major" release. +# - "major-post": after a "major" release, merge the release branch with main and +# do another devel snapshot on main (e.g. do "1.27.1-dev" release). +# - "minor" : on a stable branch do a minor release (e.g. "1.26.4" on "nm-1-26" +# branch and bump to "1.26.5-dev"). +# +# Requisites: +# +# * You need to start with a clean working directory (git clean -fdx) +# +# * Run in a "clean" environment, i.e. no unusual environment variables set, on a recent +# Fedora, with suitable dependencies installed. +# +# * First, ensure that you have ssh keys for "master.gnome.org" installed (and ssh-agent running). +# Also, ensure you have a GPG key that you want to use for signing. Also, have gpg-agent running +# and possibly configure `git config --get user.signingkey` for the proper key. +# +# * Your git repository needs a remote "origin" that points to the upstream git repository +# (or set $ORIGIN) and use the standard git refs/remotes/$ORIGIN/ branch names. +# +# * All your (relevant) local branches (main and nm-1-*) must be up to date with their +# remote tracking branches for origin. +# +# Run with --no-test to do the actual release. + +die() { + echo -n "FAIL: " + echo_color 31 "$@" + exit 1 +} + +echo_color() { + local color="$1" + shift + echo -e -n "\033[0;${color}m" + echo "$@" + echo -e -n '\033[0m' +} + +print_usage() { + echo "Usage:" + echo " $BASH_SOURCE [devel|rc1|rc|major|major-post|minor]" + echo " [--no-test] \\" + echo " [--no-find-backports] \\" + echo " [--no-cleanup] \\" + echo " [--allow-local-branches] \\" + echo " [--no-check-gitlab] \\" + echo " [--no-check-news] \\" + echo " [--no-warn-publish-docs] \\" +} + +die_help() { + print_usage + echo + sed -e '/^# /,/# Run with --no-test/!d' -e 's/^#\($\| \)/ /' "$BASH_SOURCE" + exit 0 +} + +die_usage() { + echo -n "FAIL: " + echo_color 31 "$@" + echo + print_usage + exit 1 +} + +do_command() { + local color=36 + if [ "$DRY_RUN" = 0 ]; then + color=31 + fi + echo -n "COMMAND: " + echo_color $color -n "$@" + echo + if [ "$DRY_RUN" = 0 ]; then + "$@" + fi +} + +parse_version() { + local MAJ="$(sed -n '1,20 s/^m4_define(\[nm_major_version\], \[\([0-9]\+\)\])$/\1/p' ./configure.ac)" + local MIN="$(sed -n '1,20 s/^m4_define(\[nm_minor_version\], \[\([0-9]\+\)\])$/\1/p' ./configure.ac)" + local MIC="$(sed -n '1,20 s/^m4_define(\[nm_micro_version\], \[\([0-9]\+\)\])$/\1/p' ./configure.ac)" + + re='^(0|[1-9][0-9]*) (0|[1-9][0-9]*) (0|[1-9][0-9]*)$' + [[ "$MAJ $MIN $MIC" =~ $re ]] || return 1 + echo "$MAJ $MIN $MIC" +} + +number_is_even() { + local re='^[0-9]*[02468]$' + [[ "$1" =~ $re ]] +} + +number_is_odd() { + local re='^[0-9]*[13579]$' + [[ "$1" =~ $re ]] +} + +git_same_ref() { + local a="$(git rev-parse "$1" 2>/dev/null)" || return 1 + local b="$(git rev-parse "$2" 2>/dev/null)" || return 1 + [ "$a" = "$b" ] +} + +check_gitlab_pipeline() { + local BRANCH="$1" + local SHA="$2" + local PIPELINE_ID + + PIPELINE_ID="$(curl --no-progress-meter "https://gitlab.freedesktop.org/api/v4/projects/411/pipelines?ref=$BRANCH&sha=$SHA&order_by=id" 2>/dev/null | jq '.[0].id')" + if ! [[ $PIPELINE_ID =~ [0-9]+ ]] ; then + echo "Cannot find pipeline for branch $BRANCH. Check \"https://gitlab.freedesktop.org/NetworkManager/NetworkManager/pipelines?page=1&scope=branches&ref=$BRANCH\"" + return 1 + fi + + PIPELINE_STATUSES="$(curl --no-progress-meter "https://gitlab.freedesktop.org/api/v4/projects/411/pipelines/$PIPELINE_ID/jobs?per_page=100" 2>/dev/null | jq '.[] | select(.stage!="prep" and .stage!="tier3") | .status')" + + if ! echo "$PIPELINE_STATUSES" | grep -q '^"success"$' ; then + echo "Cannot find successful jobs for branch $BRANCH. Check \"https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/pipelines/$PIPELINE_ID\"" + return 1 + fi + if echo "$PIPELINE_STATUSES" | grep -q -v '^"success"$' ; then + echo "Seems not all jobs for $BRANCH ran (or were successfull). Check \"https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/pipelines/$PIPELINE_ID\"" + return 1 + fi + + return 0 +} + +set_version_number_autotools() { + sed -i \ + -e '1,20 s/^m4_define(\[nm_major_version\], \[\([0-9]\+\)\])$/m4_define([nm_major_version], ['"$1"'])/' \ + -e '1,20 s/^m4_define(\[nm_minor_version\], \[\([0-9]\+\)\])$/m4_define([nm_minor_version], ['"$2"'])/' \ + -e '1,20 s/^m4_define(\[nm_micro_version\], \[\([0-9]\+\)\])$/m4_define([nm_micro_version], ['"$3"'])/' \ + ./configure.ac +} + +set_version_number_meson() { + sed -i \ + -e '1,20 s/^\( *version: *'\''\)[0-9]\+\.[0-9]\+\.[0-9]\+\('\'',\)$/\1'"$1.$2.$3"'\2/' \ + meson.build +} + +set_version_number() { + set_version_number_autotools "$@" && + set_version_number_meson "$@" +} + +check_news() { + local mode="$1" + shift + local ver_arr=("$@") + + case "$mode" in + major|minor) + if git grep -q 'NOT RECOMMENDED FOR PRODUCTION USE' -- ./NEWS ; then + return 1 + fi + ;; + *) + ;; + esac + return 0 +} + +DO_CLEANUP=1 +CLEANUP_CHECKOUT_BRANCH= +CLEANUP_REFS=() +cleanup() { + if [ $DO_CLEANUP = 1 ]; then + [ -n "$CLEANUP_CHECKOUT_BRANCH" ] && git checkout -f "$CLEANUP_CHECKOUT_BRANCH" + for c in "${CLEANUP_REFS[@]}"; do + echo "delete reference. Restore with $(echo_color 36 -n git update-ref \"$c\" $(git rev-parse "$c"))" + git update-ref -d "$c" + done + fi +} + +trap cleanup EXIT + +DIR="$(git rev-parse --show-toplevel)" + +ORIGIN=origin + +BASH_SOURCE_ABSOLUTE="$(readlink -f "$BASH_SOURCE")" + +test -d "$DIR" && +cd "$DIR" && +test -f ./contrib/fedora/rpm/build_clean.sh || die "cannot find NetworkManager base directory" + +RELEASE_MODE="" +DRY_RUN=1 +FIND_BACKPORTS=1 +ALLOW_LOCAL_BRANCHES=0 +HELP_AND_EXIT=1 +CHECK_GITLAB=1 +WARN_PUBLISH_DOCS=1 +CHECK_NEWS=1 +while [ "$#" -ge 1 ]; do + A="$1" + shift + HELP_AND_EXIT=0 + case "$A" in + --no-test) + DRY_RUN=0 + ;; + --no-find-backports) + FIND_BACKPORTS=0 + ;; + --no-cleanup) + DO_CLEANUP=0 + ;; + --allow-local-branches) + # by default, the script errors out if the relevant branch (main, nm-1-Y) are not the same + # as the remote branch on origin. You should not do a release if you have local changes + # that differ from upstream. Set this flag to override that check. + ALLOW_LOCAL_BRANCHES=1 + ;; + --no-check-gitlab) + CHECK_GITLAB=0 + ;; + --no-warn-publish-docs) + WARN_PUBLISH_DOCS=0 + ;; + --no-check-news) + CHECK_NEWS=0 + ;; + --help|-h) + die_help + ;; + devel|rc1|rc|major|major-post|minor) + [ -z "$RELEASE_MODE" ] || die_usage "duplicate release-mode" + RELEASE_MODE="$A" + ;; + *) + die_usage "unknown argument \"$A\"" + ;; + esac +done +[ "$HELP_AND_EXIT" = 1 ] && die_help + +[ -n "$RELEASE_MODE" ] || die_usage "specify the desired release mode" + +VERSION_ARR=( $(parse_version) ) || die "cannot detect NetworkManager version" +VERSION_STR="$(IFS=.; echo "${VERSION_ARR[*]}")" + +echo "Current version before release: $VERSION_STR (do \"$RELEASE_MODE\" release)" + +grep -q "version: '${VERSION_ARR[0]}.${VERSION_ARR[1]}.${VERSION_ARR[2]}'," ./meson.build || die "meson.build does not have expected version" + +TMP="$(git status --porcelain)" || die "git status failed" +test -z "$TMP" || die "git working directory is not clean (git status --porcelain)" + +TMP="$(LANG=C git clean -ndx)" || die "git clean -ndx failed" +test -z "$TMP" || die "git working directory is not clean? (git clean -ndx)" + +CUR_BRANCH="$(git rev-parse --abbrev-ref HEAD)" +CUR_HEAD="$(git rev-parse HEAD)" +TMP_BRANCH=release-branch + +if [ "$CUR_BRANCH" = main ]; then + number_is_odd "${VERSION_ARR[1]}" || die "Unexpected version number on main. Should be an odd development version" + [ "$RELEASE_MODE" = devel -o "$RELEASE_MODE" = rc1 -o "$RELEASE_MODE" = major-post ] || die "Unexpected branch name \"$CUR_BRANCH\" for \"$RELEASE_MODE\"" +else + re='^nm-[0-9]+-[0-9]+$' + [[ "$CUR_BRANCH" =~ $re ]] || die "Unexpected current branch $CUR_BRANCH. Should be main or nm-?-??" + if number_is_odd "${VERSION_ARR[1]}"; then + # we are on a release candiate branch. + [ "$RELEASE_MODE" = rc -o "$RELEASE_MODE" = major ] || die "Unexpected branch name \"$CUR_BRANCH\" for \"$RELEASE_MODE\"" + [ "$CUR_BRANCH" == "nm-${VERSION_ARR[0]}-$((${VERSION_ARR[1]} + 1))" ] || die "Unexpected current branch $CUR_BRANCH. Should be nm-${VERSION_ARR[0]}-$((${VERSION_ARR[1]} + 1))" + else + [ "$RELEASE_MODE" = minor ] || die "Unexpected branch name \"$CUR_BRANCH\" for \"$RELEASE_MODE\"" + [ "$CUR_BRANCH" == "nm-${VERSION_ARR[0]}-${VERSION_ARR[1]}" ] || die "Unexpected current branch $CUR_BRANCH. Should be nm-${VERSION_ARR[0]}-${VERSION_ARR[1]}" + fi +fi + +RC_VERSION= +RELEASE_BRANCH= +case "$RELEASE_MODE" in + minor) + number_is_even "${VERSION_ARR[1]}" && + number_is_odd "${VERSION_ARR[2]}" || die "cannot do minor release on top of version $VERSION_STR" + [ "$CUR_BRANCH" != main ] || die "cannot do a minor release on main" + ;; + devel) + number_is_odd "${VERSION_ARR[1]}" || die "cannot do devel release on top of version $VERSION_STR" + [ "$((${VERSION_ARR[2]} + 1))" -lt 90 ] || die "devel release must have a micro version smaller than 90 but current version is $VERSION_STR" + [ "$CUR_BRANCH" == main ] || die "devel release can only be on main" + ;; + rc) + number_is_odd "${VERSION_ARR[1]}" || die "cannot do rc release on top of version $VERSION_STR" + [ "${VERSION_ARR[2]}" -ge 90 ] || die "rc release must have a micro version larger than ${VERSION_ARR[0]}.90 but current version is $VERSION_STR" + RC_VERSION="$((${VERSION_ARR[2]} - 88))" + [ "$CUR_BRANCH" == "nm-${VERSION_ARR[0]}-$((${VERSION_ARR[1]} + 1))" ] || die "devel release can only be on \"nm-${VERSION_ARR[0]}-$((${VERSION_ARR[1]} + 1))\" branch" + ;; + rc1) + number_is_odd "${VERSION_ARR[1]}" || die "cannot do rc release on top of version $VERSION_STR" + [ "${VERSION_ARR[2]}" -lt 90 ] || die "rc release must have a micro version smaller than ${VERSION_ARR[0]}.${VERSION_ARR[1]}.90 but current version is $VERSION_STR" + [ "$CUR_BRANCH" == main ] || die "rc1 release can only be on main" + RELEASE_BRANCH="nm-${VERSION_ARR[0]}-$((${VERSION_ARR[1]} + 1))" + ;; + major) + number_is_odd "${VERSION_ARR[1]}" || die "cannot do major release on top of version $VERSION_STR" + [ "${VERSION_ARR[2]}" -ge 90 ] || die "parent version for major release must have a micro version larger than ${VERSION_ARR[0]}.90 but current version is $VERSION_STR" + [ "$CUR_BRANCH" == "nm-${VERSION_ARR[0]}-$((${VERSION_ARR[1]} + 1))" ] || die "major release can only be on \"nm-${VERSION_ARR[0]}-$((${VERSION_ARR[1]} + 1))\" branch" + ;; + major-post) + number_is_odd "${VERSION_ARR[1]}" || die "cannot do major-post release on top of version $VERSION_STR" + [ "$((${VERSION_ARR[2]} + 1))" -lt 90 ] || die "major-post release must have a micro version smaller than 90 but current version is $VERSION_STR" + [ "$CUR_BRANCH" == main ] || die "major-post release can only be on main" + ;; + *) + die "Release mode $RELEASE_MODE not yet implemented" + ;; +esac + +git fetch "$ORIGIN" || die "git fetch failed" + +if [ "$ALLOW_LOCAL_BRANCHES" != 1 ]; then + git_same_ref "$CUR_BRANCH" "refs/heads/$CUR_BRANCH" || die "Current branch $CUR_BRANCH is not a branch??" + git_same_ref "$CUR_BRANCH" "refs/remotes/$ORIGIN/$CUR_BRANCH" || die "Current branch $CUR_BRANCH seems not up to date with refs/remotes/$ORIGIN/$CUR_BRANCH. Git pull or --allow-local-branches?" +fi + +NEWER_BRANCHES=() +if [ "$CUR_BRANCH" != main ]; then + i="${VERSION_ARR[1]}" + while : ; do + i=$((i + 2)) + b="nm-${VERSION_ARR[0]}-$i" + if ! git show-ref --verify --quiet "refs/remotes/$ORIGIN/$b"; then + git show-ref --verify --quiet "refs/heads/$b" && die "unexpectedly branch $b exists" + break + fi + if [ "$ALLOW_LOCAL_BRANCHES" != 1 ]; then + git_same_ref "$b" "refs/heads/$b" || die "branch $b is not a branch??" + git_same_ref "$b" "refs/remotes/$ORIGIN/$b" || die "branch $b seems not up to date with refs/remotes/$ORIGIN/$b. Git pull or --allow-local-branches?" + fi + NEWER_BRANCHES+=("refs/heads/$b") + done + b=main + if [ "$ALLOW_LOCAL_BRANCHES" != 1 ]; then + git_same_ref "$b" "refs/heads/$b" || die "branch $b is not a branch??" + git_same_ref "$b" "refs/remotes/$ORIGIN/$b" || die "branch $b seems not up to date with refs/remotes/$ORIGIN/$b. Git pull or --allow-local-branches?" + fi +fi + +if [ -n "$RELEASE_BRANCH" ]; then + git show-ref --verify --quiet "refs/remotes/$ORIGIN/$RELEASE_BRANCH" && die "release branch refs/remotes/$ORIGIN/$RELEASE_BRANCH unexpectedly exists already" + git show-ref --verify --quiet "refs/heads/$RELEASE_BRANCH" && die "release branch refs/heads/$RELEASE_BRANCH unexpectedly exists already" +fi + +if [ "$ALLOW_LOCAL_BRANCHES" != 1 ]; then + cmp <(git show "$ORIGIN/main:contrib/fedora/rpm/release.sh") "$BASH_SOURCE_ABSOLUTE" || die "$BASH_SOURCE is not identical to \`git show \"$ORIGIN/main:contrib/fedora/rpm/release.sh\"\`" +fi + +if ! check_news "$RELEASE_MODE" "@{VERSION_ARR[@]}" ; then + if [ "$CHECK_NEWS" == 1 ]; then + die "NEWS file needs update to mention stable release (skip check with --no-check-news)" + fi + echo "WARNING: NEWS file needs update to mention stable release (test skipped with --no-check-news)" +fi + +if [ "$RELEASE_MODE" = major -o "$RELEASE_MODE" = minor ]; then + echo + latest= + if [ "$RELEASE_MODE" = major ]; then + echo "Note that after the new major you have to publish the new documentation on" + latest=" -l" + else + echo "Note that after the stable release you maybe should publish the new documentation on" + latest=" [-l]" + fi + echo "$(echo_color 36 -n "https://gitlab.freedesktop.org/NetworkManager/networkmanager.pages.freedesktop.org.git") by running" + if [ "$RELEASE_MODE" = major ]; then + v="${VERSION_ARR[0]}.$((${VERSION_ARR[1]} + 1)).0" + else + v="${VERSION_ARR[0]}.${VERSION_ARR[1]}.$((${VERSION_ARR[2]} + 1))" + fi + echo " \`$(echo_color 36 -n "./scripts/import-docs.sh $v$latest")\`" + echo + if [ $WARN_PUBLISH_DOCS = 1 ]; then + echo "Avoid this prompt via \"--no-warn-publish-docs\"" + read -p "Please confirm that you know [ENTER] " + fi +fi + +if [ $FIND_BACKPORTS = 1 ]; then + git show "$ORIGIN/main:contrib/scripts/find-backports" > ./.git/nm-find-backports \ + && chmod +x ./.git/nm-find-backports \ + || die "cannot get contrib/scripts/find-backports" + + TMP="$(./.git/nm-find-backports "$CUR_BRANCH" main "${NEWER_BRANCHES[@]}" 2>/dev/null)" || die "nm-find-backports failed" + test -z "$TMP" || die "nm-find-backports returned patches that need to be backported (ignore with --no-find-backports): ./.git/nm-find-backports \"$CUR_BRANCH\" main ${NEWER_BRANCHES[@]}" +fi + +if [ $CHECK_GITLAB = 1 ]; then + if ! check_gitlab_pipeline "$CUR_BRANCH" "$CUR_HEAD" ; then + echo "Check the pipelines for branch \"$CUR_BRANCH\" at https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/pipelines?ref=$CUR_BRANCH" + echo "Wait for pipeline with \`ci-fairy wait-for-pipeline --project NetworkManager/NetworkManager --sha \"$CUR_HEAD\"\`" + die "It seems not all gitlab-ci jobs were running/succeeding. Skip this check with --no-check-gitlab" + fi +fi + +BRANCHES=() +BUILD_TAG= + +CLEANUP_CHECKOUT_BRANCH="$CUR_BRANCH" + +git checkout -B "$TMP_BRANCH" +CLEANUP_REFS+=("refs/heads/$TMP_BRANCH") + +case "$RELEASE_MODE" in + minor) + set_version_number "${VERSION_ARR[0]}" "${VERSION_ARR[1]}" $(("${VERSION_ARR[2]}" + 1)) + git commit -m "release: bump version to ${VERSION_ARR[0]}.${VERSION_ARR[1]}.$(("${VERSION_ARR[2]}" + 1))" -a || die "failed to commit release" + set_version_number "${VERSION_ARR[0]}" "${VERSION_ARR[1]}" $(("${VERSION_ARR[2]}" + 2)) + git commit -m "release: bump version to ${VERSION_ARR[0]}.${VERSION_ARR[1]}.$(("${VERSION_ARR[2]}" + 2)) (development)" -a || die "failed to commit devel version bump" + + b="${VERSION_ARR[0]}.${VERSION_ARR[1]}.$(("${VERSION_ARR[2]}" + 1))" + git tag -s -a -m "Tag $b" "$b" HEAD~ || die "failed to tag release" + BRANCHES+=("$b") + CLEANUP_REFS+=("refs/tags/$b") + BUILD_TAG="$b" + b="${VERSION_ARR[0]}.${VERSION_ARR[1]}.$(("${VERSION_ARR[2]}" + 2))" + git tag -s -a -m "Tag $b (development)" "$b-dev" HEAD || die "failed to tag devel version" + BRANCHES+=("$b-dev") + CLEANUP_REFS+=("refs/tags/$b-dev") + TAR_VERSION="$BUILD_TAG" + ;; + devel) + set_version_number "${VERSION_ARR[0]}" "${VERSION_ARR[1]}" $(("${VERSION_ARR[2]}" + 1)) + git commit -m "release: bump version to ${VERSION_ARR[0]}.${VERSION_ARR[1]}.$(("${VERSION_ARR[2]}" + 1)) (development)" -a || die "failed to commit devel version bump" + + b="${VERSION_ARR[0]}.${VERSION_ARR[1]}.$(("${VERSION_ARR[2]}" + 1))" + git tag -s -a -m "Tag $b (development)" "$b-dev" HEAD || die "failed to tag release" + BRANCHES+=("$b-dev") + CLEANUP_REFS+=("refs/tags/$b-dev") + BUILD_TAG="$b-dev" + TAR_VERSION="$b" + ;; + rc) + b="${VERSION_ARR[0]}.${VERSION_ARR[1]}.$(("${VERSION_ARR[2]}" + 1))" + t="${VERSION_ARR[0]}.$(("${VERSION_ARR[1]}" + 1))-rc$RC_VERSION" + set_version_number "${VERSION_ARR[0]}" "${VERSION_ARR[1]}" $(("${VERSION_ARR[2]}" + 1)) + git commit -m "release: bump version to $b ($t) (development)" -a || die "failed to commit rc version bump" + + git tag -s -a -m "Tag $b ($t) (development)" "$t" HEAD || die "failed to tag release" + BRANCHES+=("$t") + CLEANUP_REFS+=("refs/tags/$t") + BUILD_TAG="$t" + TAR_VERSION="$b" + ;; + rc1) + set_version_number "${VERSION_ARR[0]}" "${VERSION_ARR[1]}" 90 + b="${VERSION_ARR[0]}.${VERSION_ARR[1]}.90" + t="${VERSION_ARR[0]}.$(("${VERSION_ARR[1]}" + 1))-rc1" + git commit -m "release: bump version to $b ($t)" -a || die "failed to commit rc1 version bump" + + git tag -s -a -m "Tag $b ($t) (development)" "$t" HEAD || die "failed to tag release $t" + BRANCHES+=("$t") + CLEANUP_REFS+=("refs/tags/$t") + BUILD_TAG="$t" + TAR_VERSION="$b" + ;; + major) + b="${VERSION_ARR[0]}.$((${VERSION_ARR[1]} + 1)).0" + b2="${VERSION_ARR[0]}.$((${VERSION_ARR[1]} + 1)).1" + + set_version_number "${VERSION_ARR[0]}" "$((${VERSION_ARR[1]} + 1))" 0 + git commit -m "release: bump version to $b" -a || die "failed to commit major version bump" + git tag -s -a -m "Tag $b" "$b" HEAD || die "failed to tag release" + BRANCHES+=("$b") + CLEANUP_REFS+=("refs/tags/$b") + + set_version_number "${VERSION_ARR[0]}" "$((${VERSION_ARR[1]} + 1))" 1 + git commit -m "release: bump version to $b2 (development)" -a || die "failed to commit another bump after major version bump" + git tag -s -a -m "Tag $b (development)" "$b2-dev" HEAD || die "failed to tag release" + BRANCHES+=("$b2-dev") + CLEANUP_REFS+=("refs/tags/$b2-dev") + + BUILD_TAG="$b" + TAR_VERSION="$b" + ;; + major-post) + # We create a merge commit with the content of current "main", with two + # parent commits $THE_RELEASE and "main". But we want that the first parent + # is the release, so that `git log --first-parent` follows the path with the + # release candidates, and not the devel part during that time. Hence this + # switcheroo here. + git checkout -B "$TMP_BRANCH" "${VERSION_ARR[0]}.$((${VERSION_ARR[1]} - 1)).0" || die "merge0" + git merge -Xours --commit -m tmp main || die "merge1" + git rm --cached -r . || die "merge2" + git checkout main -- . || die "merge3" + b="${VERSION_ARR[0]}.${VERSION_ARR[1]}.$((${VERSION_ARR[2]} + 1))" + git commit --amend -m tmp -a || die "failed to commit major version bump" + test x = "x$(git diff main HEAD)" || die "there is a diff after merge!" + + set_version_number "${VERSION_ARR[0]}" "${VERSION_ARR[1]}" "$((${VERSION_ARR[2]} + 1))" + git commit --amend -m "release: bump version to $b (development)" -a || die "failed to commit major version bump" + git tag -s -a -m "Tag $b (development)" "$b-dev" HEAD || die "failed to tag release" + BRANCHES+=("$b-dev") + CLEANUP_REFS+=("refs/tags/$b-dev") + BUILD_TAG="$b-dev" + TAR_VERSION="$b" + ;; + *) + die "Release mode $RELEASE_MODE not yet implemented" + ;; +esac + +build_tag() { + git checkout "$BUILD_TAG" || die "failed to checkout $BUILD_TAG" + + ./contrib/fedora/rpm/build_clean.sh -r || die "build release failed" + + test -f "./build/meson-dist/$RELEASE_FILE" \ + || die "release file \"./build/meson-dist/$RELEASE_FILE\" not found" + + cp "./build/meson-dist/$RELEASE_FILE" /tmp/ || die "failed to copy release tarball to /tmp" + + if test -f "./build/meson-dist/$RELEASE_FILE.sig" ; then + cp "./build/meson-dist/$RELEASE_FILE.sig" /tmp/ || die "failed to copy signature for tarball to /tmp" + fi + + git clean -fdx +} + +RELEASE_FILES=() +if [ -n "$BUILD_TAG" ]; then + RELEASE_FILE="NetworkManager-$TAR_VERSION.tar.xz" + RELEASE_FILES+=("$RELEASE_FILE") + build_tag +fi +git checkout -B "$CUR_BRANCH" "$TMP_BRANCH" || die "cannot checkout $CUR_BRANCH" + +BRANCHES+=( "$CUR_BRANCH" ) + +if [ "$RELEASE_MODE" = rc1 ]; then + git branch "$RELEASE_BRANCH" "$TMP_BRANCH" || die "cannot checkout $CUR_BRANCH" + BRANCHES+=( "$RELEASE_BRANCH" ) + CLEANUP_REFS+=( "refs/heads/$RELEASE_BRANCH" ) +fi + +if [ "$RELEASE_MODE" = rc1 ]; then + git checkout "$TMP_BRANCH" + b="${VERSION_ARR[0]}.$((${VERSION_ARR[1]} + 2)).0" + set_version_number "${VERSION_ARR[0]}" "$((${VERSION_ARR[1]} + 2))" 0 + git commit -m "release: bump version to $b (development)" -a || die "failed to commit devel version bump" + git tag -s -a -m "Tag $b (development)" "$b-dev" HEAD || die "failed to tag release" + BRANCHES+=("$b-dev") + CLEANUP_REFS+=("refs/tags/$b-dev") + BUILD_TAG="$b-dev" + TAR_VERSION="$b" + RELEASE_FILE="NetworkManager-$TAR_VERSION.tar.xz" + RELEASE_FILES+=("$RELEASE_FILE") + build_tag + git checkout -B "$CUR_BRANCH" "$TMP_BRANCH" || die "cannot checkout $CUR_BRANCH" +fi + +if ! [ "$DRY_RUN" = 0 ]; then + ssh master.gnome.org true || die "failed to \`ssh master.gnome.org\`" +fi + +for r in "${RELEASE_FILES[@]}"; do + do_command rsync -va --append-verify -P "/tmp/$r" master.gnome.org: || die "failed to rsync \"/tmp/$r\"" +done + +do_command git push "$ORIGIN" "${BRANCHES[@]}" || die "failed to to push branches ${BRANCHES[@]} to $ORIGIN" + +FAIL=0 +for r in "${RELEASE_FILES[@]}"; do + do_command ssh master.gnome.org ftpadmin install --unattended "$r" || FAIL=1 +done +if [ "$FAIL" = 1 ]; then + die "ftpadmin install failed. This was the last step. Invoke the command manually" +fi + +CLEANUP_CHECKOUT_BRANCH= +if [ "$DRY_RUN" = 0 ]; then + CLEANUP_REFS=() + git branch -D "$TMP_BRANCH" +else + H="$(git rev-parse "$CUR_BRANCH")" + git checkout -B "$CUR_BRANCH" "$CUR_HEAD" || die "cannot reset $CUR_BRANCH to $CUR_HEAD" + echo "delete reference. Restore with $(echo_color 36 -n git checkout -B "\"$CUR_BRANCH\"" "$H")" +fi diff --git a/contrib/fedora/utils/makerepo.sh b/contrib/fedora/utils/makerepo.sh new file mode 100755 index 00000000..266aac8f --- /dev/null +++ b/contrib/fedora/utils/makerepo.sh @@ -0,0 +1,698 @@ +#!/bin/bash + +# +# The script is ugly but is here to help to create a git-repository +# based on dist-git. +# +# * Works with fedpkg and rhpkg +# * Different packages are supported. See detect_build_type below. +# * Creates first an initial commit of the source directory (after "fedpkg prep") +# * Excludes files and creates a gitignore file. It does so by .git/makerepo.gitignore +# which can be edited manually. Also, after a `$0 local`, it will record all files +# with modifications to be ignored in the future. +# * Revert each patch from the spec file +# * Reapply each patch until you are where were originally (sans ignored files) +# * Restore again the original state, i.e. replying the patches (including ignored +# files -- that are no longer part of master-tip). +# * Fetch from upstream origin (and add as remote) +# * Fetch from a local git repository (and add as remote) +# * It can detect the parent commit where the package branched of +# and rebase the created history on top of that. +# * optionally, do `fedpkg local`. +# +# ONE-TIME SETUP: +# - clone the dist-git package +# $ PACKAGE=libnl3 +# $ fedpkg clone $PACKAGE +# $ cd $PACKAGE +# +# - configure local git-repository (optional) +# $ ln -s /path/to/local/clone .git/local +# +# - create initial gitignore file (optional) +# $ edit .git/makerepo.gitignore +# or +# $ edit .git/makerepo.gitignore.$BRANCHNAME +# +# USAGE: +# $ cd $PACKAGE +# $ makerepo.sh +# $ makerepo.sh local +# + + +#set -vx + +die() { + echo "$@" >&2 + exit 1 +} + +containsElement () { + local e + local name="$1" + shift + local i=0 + + for e in "${@:2}"; do + if [[ "$e" == "$1" ]]; then + eval "$name=$i" + return 0; + fi + i=$((i+1)) + done + return 1 +} + +git_remote_add_gnome() { + git remote add "${2-origin}" "https://gitlab.gnome.org/GNOME/$1.git" && \ + git remote 'set-url' --push "${2-origin}" "git@gitlab.gnome.org:GNOME/$1.git" +} + +git_remote_add_github() { + git remote add "${2-origin}" "https://github.com/$1.git" + git remote 'set-url' --push "${2-origin}" "git@github.com:$1.git" +} + +srcdir="$(readlink -f "$(git rev-parse --show-toplevel 2>/dev/null)")" +[[ "x$srcdir" != x ]] || die "Could not detect dist-git directory (are you inside the git working directory?)" +cd "$srcdir" || die "Could not switch to dist-git directory" + + +if [[ "x$(ls -1d ./*.spec 2>/dev/null)" == x || ! -f "./sources" ]]; then + die "**Error**: Directory "\`$srcdir\'" does not look like the dist-git pkg dir." +fi + +if [[ "$FEDPKG" == "" ]]; then + REMOTE="$(git config --get "branch.$(git branch --show-current).remote" 2>/dev/null)" + URL="$(git config --get "remote.$REMOTE.url")" + if [[ "$URL" = *'pkgs.devel.redhat.com'* ]]; then + FEDPKG=rhpkg + elif [[ "$URL" = *'gitlab.com'*'redhat/centos-stream'* ]]; then + FEDPKG=centpkg + elif [[ "$URL" = *'pkgs.fedoraproject.org/'* || "$URL" = *'src.fedoraproject.org/'* ]]; then + FEDPKG=fedpkg + else + die "not inside dist-git repository? Check out a branch that has the dist-git remote tracking branch >>$PWD<<" + fi +fi + +split_patch() { + # patches created with git-format-patch that contain more then one + # commit, cannot be easily reverted with patch, because patch works + # the patches from top down. In case of -R however, we have to apply + # the latest patches first. + + read -r -d '' PERL_PROG <<-'EOF' + use strict; + use warnings; + + open FILE, $ARGV[0] or die "Can't open $ARGV[0] for reading: $!\n"; + + local $/ = undef; + my $file = <FILE>; + close FILE; + + my @patches = split(/\n\nFrom /,$file); + + my $i = $#patches + 1; + my $patch; + my $first = 1; + foreach $patch (@patches){ + if ($first) { + $first = 0; + } else { + $patch = "From $patch" + } + my $o = sprintf("%s%s%03d", $ARGV[0], $ARGV[1], $i); + open(my $OUT, ">", $o) or die "Can't open $o for writing: $!"; + $i--; + + print $OUT "$patch"; + + close $OUT; + } + EOF + + perl -e "$PERL_PROG" "$1" "$2" +} + +spec_parse_patch_p() { + local SPEC="$1" + local NUM="$2" + + local P="$(sed -n "s/^%\<patch$NUM\>.* -p\([0-9]\+\) .*$/\1/p" "$SPEC")" + + echo "${P:-1}" +} + +get_patch_origin() { + local PATCH="$1" + + ( + cd "$srcdir" + + local HASH="$(git log -n1 --format="%H" HEAD -- "$PATCH")" + + if [[ "$HASH" == "" ]]; then + return + fi + + printf "\n\nPatch \"%s\" was last modified in commit:\n\n" "$PATCH" + git log -n1 "$HASH" | sed 's/^[^ ]/ \0/' + ) +} + +print_synopsis() { + echo "SYNOPSIS: $(basename "$0") [--dist|-d DIST] [local|--local|-l] [-?|-h|--help|help] [NUM]" + echo " - If [NUM] is omitted, it will revert all patches from the spec file," + echo " otherwise only the last NUM patches." + echo " - When specifying 'local', it will also call \`$FEDPKG local\` to configure" + echo " and build the output directory." + echo " - '--dist' implies '--local'. This argument is passed to ${FEDPKG}." + echo " TIP: symlink your local git clone of upstream to './.git/local'." +} + +unset REVERT_COUNT +LOCAL=0 +DIST="" +while [ $# -ne 0 ]; do + ARG="$1" + shift + case "$ARG" in + -h|'-?'|help|--help) + print_synopsis + exit 0 + ;; + local|--local|-l) + LOCAL=1 + ;; + --dist|-d) + DIST="$1" + shift + if [ "x$DIST" = x ]; then + print_synopsis + die "--dist needs an argument" + fi + ;; + *) + if [ -n "${REVERT_COUNT+x}" ]; then + print_synopsis + die "invalid argument \"$ARG\"" + fi + case "$ARG" in + ''|*[!0-9]*) + print_synopsis + die "invalid argument \"$ARG\": should be an integer (number of patches to revert)" + ;; + esac + REVERT_COUNT="$ARG" + ;; + esac +done + +if [ "x$DIST" != x ]; then + DIST=" --dist $DIST" +fi + +# generate the clean dir +$FEDPKG $DIST prep || die "error while \`$FEDPKG$DIST prep\`" + +detect_build_type() { + local TEST_DIR="./$1/" + local TEST_SPEC="$2" + local TEST_BUILD_TYPE="$3" + local DIRNAME + + if [[ -n "$BUILD_TYPE" || -z "$1" || "x$(ls -1d $TEST_DIR 2>/dev/null)" == x || ! -f "$TEST_SPEC" ]]; then + return 1 + fi + + DIRNAME="$(ls -1d $TEST_DIR)" || die "could not find directory" + DIRNAME="$(basename "$DIRNAME")" + SPEC="$TEST_SPEC" + + if [[ -n "$TEST_BUILD_TYPE" ]]; then + BUILD_TYPE="$TEST_BUILD_TYPE" + else + BUILD_TYPE="${TEST_SPEC%.spec}" + fi +} + +detect_dirname() { + local BUILD_TYPE="$1" + local DIRS=() + local SOURCES + local D suffix T + + SOURCES="$(sed 's/^\(SHA512 (\(.*\)) = [0-9a-f]\{128\}\|\([0-9a-f]\{32\} \+\(.*\)\)\)$/\2\4/' ./sources 2>/dev/null)" + + for suffix in .tar.gz .tar.bz .tar.xz .tgz .tar.bz2 ; do + for T in ${SOURCES[@]}; do + if [[ "$T" == *$suffix ]]; then + D="${T%$suffix}" + [[ -d "$D" ]] && DIRS=("${DIRS[@]}" "$D") + D="$(tar -tf "$T" | sed 's#/.*##' | sort | uniq)" + [[ -d "$D" ]] && DIRS=("${DIRS[@]}" "$D") + fi + done + + # iterate over all tarballs that start with "$BUILD_TYPE" and + # see if there exists a directory with the same name as + # the unpacked tarball (that is, stripping the suffix). + for T in $(ls -1 "$BUILD_TYPE"*"$suffix" 2>/dev/null); do + D="${T%$suffix}" + [[ -d "$D" ]] && DIRS=("${DIRS[@]}" "$D") + done + done + + D= + if [[ ${#DIRS[@]} -ge 1 ]]; then + # return the newest directory. + D="$(ls -1d --sort=time --time=ctime "${DIRS[@]}" 2>/dev/null | head -n1)" + fi + if [[ "$D" != "" ]]; then + printf "%s" "$D" + return 0 + fi + return 1 +} + +BUILD_TYPE= +detect_build_type 'NetworkManager-[0-9]*' NetworkManager.spec +detect_build_type 'network-manager-applet-[0-9]*' network-manager-applet.spec +detect_build_type 'libnl-[0-9]*' libnl3.spec +detect_build_type 'NetworkManager-openvpn-[0-9]*' NetworkManager-openvpn.spec +detect_build_type 'NetworkManager-openswan-[0-9]*' NetworkManager-openswan.spec +detect_build_type 'NetworkManager-libreswan-[0-9]*' NetworkManager-libreswan.spec +detect_build_type 'NetworkManager-vpnc-[0-9]*' NetworkManager-vpnc.spec +detect_build_type 'ModemManager-[0-9]*' ModemManager.spec +detect_build_type 'wireless_tools.[0-9]*' wireless-tools.spec +detect_build_type 'umip-[0-9]*' mipv6-daemon.spec +detect_build_type 'initscripts-[0-9]*' initscripts.spec +detect_build_type 'libqmi-[0-9]*' libqmi.spec +detect_build_type 'libibverbs-[0-9]*' libibverbs.spec +detect_build_type 'iproute2-*' iproute.spec +detect_build_type 'glib-2*' glib2.spec +detect_build_type 'vpnc-*' vpnc.spec +detect_build_type 'gnome-control-center-*' control-center.spec gnome-control-center + +if [[ -z "$BUILD_TYPE" ]]; then + SPEC="$(ls -1 *.spec 2>/dev/null | head -n1)" + BUILD_TYPE="${SPEC%.spec}" + [[ -n "$BUILD_TYPE" ]] || die "Failed to detect repository type (no spec file)" + + [[ -f sources ]] || die "Failed to detect repository type (no sources file)" +fi + +DIRNAME="$(detect_dirname "$BUILD_TYPE")" || die "Failed to detect repository type (no directory)." + +CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)" +if [[ "x$CURRENT_BRANCH" != x && -f "./.git/makerepo.gitignore-$CURRENT_BRANCH" ]]; then + MAKEREPO_GIT_IGNORE_MY="makerepo.gitignore-$CURRENT_BRANCH" +elif [[ -f ./.git/makerepo.gitignore ]]; then + MAKEREPO_GIT_IGNORE_MY=makerepo.gitignore +else + MAKEREPO_GIT_IGNORE_MY="" +fi +MAKEREPO_GIT_IGNORE_LAST="makerepo.gitignore.last-$CURRENT_BRANCH" + +get_local_mirror() { + local URL="$1" + local DIRNAME + local FULLNAME + + if [[ -z "$URL" ]]; then + return + fi + + [[ -n "$NO_REMOTE" ]] && return + + DIRNAME="${URL##*/}" + DIRNAME="${DIRNAME%.git}" + FULLNAME="$srcdir/.git/.makerepo-${DIRNAME}.git" + + if [ ! -d "$FULLNAME" ] && [ -d "$FULLNAME.git" ]; then + # due to a bug, old versions of the script might have created "*.git.git/" directories. + # rename. + mv "$FULLNAME.git" "$FULLNAME" + fi + + if [[ ! -d "$FULLNAME" ]]; then + if [[ -f "$FULLNAME" ]]; then + # create a file with name $FULLNAME, to suppress local mirroring + return + fi + git clone --mirror --bare "$URL" "$FULLNAME/" + fi + ( + cd "$FULLNAME" + git fetch origin --prune + git gc + ) + echo "$FULLNAME" +} + +pushd "$DIRNAME" + git init . + # if you have a local clone of upstream, symlink it as ../.git/local. + if [[ "$BUILD_TYPE" == "NetworkManager" ]]; then + git remote add origin "https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git" + git remote 'set-url' --push origin "git@gitlab.freedesktop.org:NetworkManager/NetworkManager.git" + git config notes.displayRef refs/notes/bugs + git config --add remote.origin.fetch refs/tags/*:refs/tags/* + git config --add remote.origin.fetch refs/notes/bugs:refs/notes/bugs + elif [[ "$BUILD_TYPE" == "ModemManager" ]]; then + git remote add origin "git://anongit.freedesktop.org/ModemManager/ModemManager" + git remote 'set-url' --push origin "ssh://$USER@git.freedesktop.org/git/ModemManager/ModemManager" + git config --add remote.origin.fetch refs/tags/*:refs/tags/* + elif [[ "$BUILD_TYPE" == "libnl3" ]]; then + git_remote_add_github thom311/libnl + elif [[ "$BUILD_TYPE" == "network-manager-applet" || + "$BUILD_TYPE" == "gnome-control-center" || + "$BUILD_TYPE" == "libnma" || + "$BUILD_TYPE" == "NetworkManager-fortisslvpn" || + "$BUILD_TYPE" == "NetworkManager-libreswan" || + "$BUILD_TYPE" == "NetworkManager-openconnect" || + "$BUILD_TYPE" == "NetworkManager-openvpn" || + "$BUILD_TYPE" == "NetworkManager-pptp" || + "$BUILD_TYPE" == "NetworkManager-vpnc" ]]; then + git_remote_add_gnome "$BUILD_TYPE" + elif [[ "$BUILD_TYPE" == "glib2" ]]; then + git_remote_add_gnome glib + elif [[ "$BUILD_TYPE" == "NetworkManager-openswan" ]]; then + git remote add origin "git://git.gnome.org/network-manager-openswan"; + git remote 'set-url' --push origin "ssh://$USER@git.gnome.org/git/network-manager-openswan" + elif [[ "$BUILD_TYPE" == "wpa_supplicant" ]]; then + git remote add origin "git://w1.fi/hostap.git" + git_remote_add_github NetworkManager/hostap nm + elif [[ "$BUILD_TYPE" == "mipv6-daemon" ]]; then + git remote add origin "git://git.umip.org/umip.git"; + elif [[ "$BUILD_TYPE" == "libqmi" ]]; then + git remote add origin 'git://anongit.freedesktop.org/libqmi'; + elif [[ "$BUILD_TYPE" == "libibverbs" ]]; then + git remote add origin 'git://git.kernel.org/pub/scm/libs/infiniband/libibverbs.git'; + elif [[ "$BUILD_TYPE" == "initscripts" ]]; then + git remote add origin "https://git.fedorahosted.org/git/initscripts.git"; + elif [[ "$BUILD_TYPE" == "iproute" ]]; then + git remote add origin "git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git" + elif [[ "$BUILD_TYPE" == "dracut" ]]; then + git remote add origin "https://github.com/dracutdevs/dracut.git" + elif [[ "$BUILD_TYPE" == "systemd" ]]; then + git remote add origin "https://github.com/systemd/systemd.git" + elif [[ "$BUILD_TYPE" == "vpnc" ]]; then + git_remote_add_github ndpgroup/vpnc + elif [[ "$BUILD_TYPE" == "cloud-init" ]]; then + git remote add origin "https://git.launchpad.net/cloud-init" + elif [[ "$BUILD_TYPE" == "firewalld" ]]; then + git remote add origin "https://github.com/firewalld/firewalld.git" + elif [[ "$BUILD_TYPE" == "nftables" ]]; then + git remote add origin "git://git.netfilter.org/nftables" + elif [[ "$BUILD_TYPE" == "ulogd" ]]; then + git remote add origin "https://git.netfilter.org/ulogd2" + elif [[ "$BUILD_TYPE" == "libnetfilter_log" ]]; then + git remote add origin "https://git.netfilter.org/$BUILD_TYPE" + fi + LOCAL_MIRROR_URL="$(LANG=C git remote -v | sed -n 's/^origin\t*\([^\t].*\) (fetch)/\1/p')" + LOCAL_MIRROR="$(get_local_mirror "$LOCAL_MIRROR_URL")" + if [[ -n "$LOCAL_MIRROR" ]]; then + git remote add local-mirror "$LOCAL_MIRROR" + git fetch local-mirror + fi + LOCAL_GIT="$(readlink -f ../.git/local/)" + if [[ -d "$LOCAL_GIT" ]]; then + git remote add local "$LOCAL_GIT/" + git fetch local + fi + if [[ "$(git remote | grep '^origin$')x" != x && -z "$NO_REMOTE" ]]; then + git fetch origin + if [[ -n "$LOCAL_MIRROR" ]]; then + git remote rm local-mirror + fi + fi + git commit --allow-empty -m '*** empty initial commit' # useful, to rebase the following commit + git add -f -A . + git commit -m '*** add all' + git tag -f ALL + ORIG_HEAD="`git rev-parse HEAD`" + if [[ "x$RELEASE_BASE_COMMIT" == x ]]; then + # if RELEASE_BASE_COMMIT is not set, try detecting the BASE_COMMIT... + + if [[ "$BUILD_TYPE" == "NetworkManager" || + "$BUILD_TYPE" == "NetworkManager-fortisslvpn" || + "$BUILD_TYPE" == "NetworkManager-libreswan" || + "$BUILD_TYPE" == "NetworkManager-pptp" || + "$BUILD_TYPE" == "NetworkManager-openconnect" || + "$BUILD_TYPE" == "NetworkManager-vpnc" ]]; then + RELEASE_BASE_COMMIT="$(sed -n 's/^NM_GIT_SHA=\(.*\)/\1/p' configure 2>/dev/null)" + elif [[ "$BUILD_TYPE" == "libnl3" ]]; then + RELEASE_BASE_COMMIT="$(sed -n 's/^LIBNL_GIT_SHA=\(.*\)/\1/p' configure 2>/dev/null)" + if [[ "$RELEASE_BASE_COMMIT" == "23c44dad998f72f39fd1fc24aa9579fd0a7f05c0" ]]; then + RELEASE_BASE_COMMIT="e01b9df629e2f4f833fdc4fe0bda460bb738d136" + fi + elif [[ "$BUILD_TYPE" == "network-manager-applet" || + "$BUILD_TYPE" == "libnma" ]]; then + RELEASE_BASE_COMMIT="$(sed -n 's/^NMA_GIT_SHA=\(.*\)/\1/p' configure 2>/dev/null)" + if [[ "$RELEASE_BASE_COMMIT" == "8d8e34f22d5fae476eda96cf36d828c3ae8b63d3" ]]; then + RELEASE_BASE_COMMIT="a2377d7534780b96a32405cce2e5548e81bbd081" + fi + elif [[ "$BUILD_TYPE" == "glib2" ]]; then + RELEASE_BASE_COMMIT="$(git rev-parse --verify -q "$(sed 's/.*\<glib-\([0-9]\+\.[0-9]\+\.[0-9]\+\)\.[a-z0-9_.]\+\>.*$/\1/' ../sources)^{commit}" 2>/dev/null)" + elif [[ "$BUILD_TYPE" == "iproute" ]]; then + RELEASE_BASE_COMMIT="$(git rev-parse --verify -q "$(sed 's/.*\<iproute2-\([0-9]\+\.[0-9]\+\.[0-9]\+\)\..*/v\1/' ../sources)^{commit}" 2>/dev/null)" + elif [[ "$BUILD_TYPE" == "NetworkManager-openvpn" ]]; then + RELEASE_BASE_COMMIT="$(sed -n 's/^NM_GIT_SHA=\(.*\)/\1/p' configure 2>/dev/null)" + if [[ "x$RELEASE_BASE_COMMIT" == x ]]; then + DATE="$(sed -n 's/%global snapshot .git\(20[0-3][0-9]\)\([0-1][0-9]\)\([0-3][0-9]\)/\1-\2-\3/p' "../$SPEC")" + if [[ "x$DATE" != x ]]; then + RELEASE_BASE_COMMIT="$(git rev-list -n1 --date-order --before="$DATE" origin/master 2>/dev/null)" + fi + fi + fi + if [[ "x$RELEASE_BASE_COMMIT" == x ]]; then + KNOWN_BASE_COMMITS="$(cat <<EOF +# NetworkManager +08670c9163a5d0f15c57c7891ef899eb125d9423 7251704430cb206f2c29bfebc45bd0fb *NetworkManager-0.9.9.0.git20131003.tar.bz2 + +# ModemManager +397761c9758c3a8c2d130afaf36dab645d6e0ecf d9d93d2961ee35b4cd8a75a6a8631cb4 ModemManager-1.6.0.tar.xz +b23413a064f03fb2f2214fb32164bcb4b7037c45 67160b94c0eda90ebf95d1b620229ca1 ModemManager-1.6.10.tar.xz +526ec556bdf440fce3c48d3127836cf9d0b4501b SHA512 (ModemManager-1.10.8.tar.xz) = c021939322be39e102371219e648d6acb3bc5b48cf570e02113ce559321155bfe5476b4012fd95f878c0a5c5d3b9d88fb19e95adec16a5b62a01581915a39f71 + +# libnl3 +1a510c57e905c4beb06122b9688162c82d9b044f d1111959652bd6ad87b2071f61c8c20c *libnl-doc-3.2.24.tar.gz +83c762d7cf6a6c54831e8d684b22804f497704c4 6fe7136558a9071e70673dcda38545b3 *libnl-3.2.21.tar.gz +c4d846f239036c05f516c1c71789e980b64b1e70 2e1c889494d274aca24ce5f6a748e66e *libnl-3.2.22.tar.gz +0446731124bea8c1b447cc52a5ad5ae5750810ff 636769646f5b81b0caead81eab151b45 *libnl-3.2.25-rc1.tar.gz +bd0e87b3d81d2498c3f35d5497771828bf04e017 e34999eaa184c84b315a8dff8afa4219 libnl-3.2.28-rc1.tar.gz +656f381ccf58785319bb0236595c896125d33ed0 bab12db1eb94a42129f712a44be91a67 libnl-3.2.28.tar.gz + +# NetworkManager-applet +5d4f17e205f71972d4143f9760426a366b4129d7 9cc0e383c216d4bc31622a0cfb53aaa7 *network-manager-applet-0.9.9.0.git20140123.5d4f17e.tar.bz2 +36c868498f09eacafcdce9d6b68ca5aeffaae899 3146f3ac3c30996a96cd2c602fbc81e1 *network-manager-applet-0.9.10.3.git20150511.36c8684.tar.bz2 +2d5b36cf69ea6d5e11726d479012c8ad7d6fd9fc 7fc2ed3f0c46ed41ddabe99d51513b1c *network-manager-applet-1.0.4.tar.xz + +# NetworkManager-libreswan, NetworkManager-openswan +64c90fd50e57854a3fff3784b92814ffa8159b05 6a373868f85ac3b7c953f7fd6c76e637 *NetworkManager-openswan-0.9.8.0.tar.xz +78555150e4df29eb39fa4a105f884f53b0f4523f df9144805f37dc30dfaeab8da762f615 *NetworkManager-openswan-1.0.6.tar.xz +3ef831cf25e86675f9838bf58b1cd6e592c6e14f 01248eb95a1e1d647057a45aed85a3af *NetworkManager-libreswan-1.2.4.tar.xz + +# NetworkManager-vpnc +89bdcd324f2e257eca59168a7d0be5608438aab0 abb26a6c3c8d6c1d91c78471aff86b3a *NetworkManager-vpnc-0.9.8.2.tar.xz +c37a79d43ebe1192ba8dcc5036cd668631b6473e d87db7021629cef7c110a371dd42b7a8 *NetworkManager-vpnc-0.9.9.0.git20140131.tar.bz2 +68ca41550f9289835ea9d80e1ee059322ebe749a 4c16379738264a117d09c171c645ff23 *NetworkManager-vpnc-1.2.2.tar.xz + +# NetworkManager-openvpn +1f159f30617e4a3b8121074b8bf238312941370d 511eae0d4ac17c6d2659a3da2646296f *NetworkManager-openvpn-1.0.2.tar.xz +75585a94b394c04e45a28d2b032fe83dcdaeebee ee4c09a8896eab3e1740f7c7bc1434f9 NetworkManager-openvpn-1.2.4.tar.xz + +# mipv6-daemon +428974c2d0d8e75a2750a3ab0488708c5dfdd8e3 8e3ebd242e7926822bbdf5ce77c1d076 *mipv6-daemon-1.0.tar.gz + +# libqmi +7d688f382f9756027bf92338e413e425365d2835 17d6c2b404ee1eb4d1e60050fef64491 *libqmi-1.6.0.tar.xz +49abf405f5e9f16542476dceeb20de6029edcf1c SHA512 (libqmi-1.24.0.tar.xz) = e899765e67c1db0f758030e78b296015c476f938bb2afa01594b3e71a0b8d5fc2237c8272497aec891d5555523ecf0fecd69c4d0e14165c07072780621b3b502 + +# gnome-control-center +e87e0361b117f055ace2aa47cdddd0dc62a852f9 da949e268254af6aafdda0e8c1702384 *gnome-control-center-3.22.1.tar.xz + +# wpa_supplicant +22760dd94722a61175ff90c59d88c4cda1ed5e23 3be2ebfdcced52e00eda0afe2889839d *wpa_supplicant-2.0.tar.gz + +# libibverbs +990ca025d0ad967b6f266bae700bf82a4ceaff1a 1fe85889c8bbc4968b1feba6524ca408 *libibverbs-1.1.8.tar.gz + +# initscripts +cc304f05edab6c408a0f061eb1a104f9f06b8587 86ef789876b65c61751ce854835b91d4 initscripts-9.49.35.tar.bz2 + +# dracut +00efe708cab023bfe6eaf530d8ac8ea97b440de2 SHA512 (dracut-050.tar.xz) = 9d9a66acfd6b9d2fd50855a59a2393e0602c2ef97119db046f68d6167ea84d1423fa465b8b4d96339febb38d5a96df26dec7862c4b3397c3d726db18d280eee4 + +# systemd +903dd65b5eb63257393955cb79777beb8c71afc1 SHA512 (systemd-253-rc1.tar.gz) = aaf0a6bf21bbc50a42015c9cb17f69d1aaf6cab6cabfba5140a94212fb864e38d638dace9a70447f62b4d2a817a0d3bd6f4ae8d9b3c2e741cdeb1cb332f70b65 + +# libnetfilter_log +97866a0a7482ca518bad39536c7c667bfb9604b2 2a4bb0654ae675a52d2e8d1c06090b94 libnetfilter_log-1.0.1.tar.bz2 +b0e4be94c0b8f68d4e912402b93a130063c34e17 SHA512 (libnetfilter_log-1.0.2.tar.bz2) = 6b33718b1dd7f4504bceae14001da3a652cec46a6725a5dee83a7b55028cfa8e768cba917f968a5d5b60fd9ff04edf6040ef271a68e5fb65858bf73f4f9ccf23 + +# ulogd +79aa980f2df9dda0c097e8f883a62f414b9e5138 SHA512 (ulogd-2.0.8.tar.bz2) = 9f99f6f35bad5da4559d788dc3ba3dae17d4ae972737cae3313ecf68f08eaf5f55514fce6f30503437e4158fd30a06438b9249d5d20f6343964cbf690f87309d +EOF +)" + OLDIFS="$IFS" + IFS=$'\n' + for KNOWN_BASE_COMMIT in $KNOWN_BASE_COMMITS; do + MATCH="$(echo "$KNOWN_BASE_COMMIT" | sed -n 's/^[0-9a-f]\{40\} \+\(.*\)$/\1/p')" + if [[ "x$MATCH" == x ]]; then + continue + fi + if grep -q "$MATCH" ../sources; then + RELEASE_BASE_COMMIT="$(echo "$KNOWN_BASE_COMMIT" | awk '{print $1}')" + break + fi + done + IFS="$OLDIFS" + fi + fi + if [[ x != "x$RELEASE_BASE_COMMIT" ]]; then + if [[ "$RELEASE_BASE_COMMIT" == "-" ]]; then + # you can disable detection of the RELEASE_BASE_COMMIT by setting it to '-' + RELEASE_BASE_COMMIT= + else + # verify the base commit... + RELEASE_BASE_COMMIT2="$(git rev-parse --verify -q "$RELEASE_BASE_COMMIT^{commit}" 2>/dev/null)" + [[ x == "x$RELEASE_BASE_COMMIT2" ]] && test -z "$NO_REMOTE" && die "error detecting RELEASE_BASE_COMMIT=$RELEASE_BASE_COMMIT" + RELEASE_BASE_COMMIT="$RELEASE_BASE_COMMIT2" + fi + fi + if [[ x != "x$RELEASE_BASE_COMMIT" ]]; then + git checkout -B master "$RELEASE_BASE_COMMIT" || die "could not checkout master" + git tag -f BASE + git rm --cached -r :/ + git checkout "$ORIG_HEAD" -- :/ + git clean -fdx :/ + git commit -m '*** add all' + git tag -f ALL + [[ x == "x$(git diff HEAD "$ORIG_HEAD")" ]] || die "error recreating initial tarball" + fi + ( + if [[ -n "$MAKEREPO_GIT_IGNORE_MY" ]]; then + cat "../.git/$MAKEREPO_GIT_IGNORE_MY" + fi + if [[ -f "../.git/$MAKEREPO_GIT_IGNORE_LAST" ]]; then + cat "../.git/$MAKEREPO_GIT_IGNORE_LAST" + fi + sed -n 's/^%patch\([0-9]\+\) \+.*-b \+\([^ ]\+\).*$/*\2/p' ../"$SPEC"; + echo '*.[0-9][0-9][0-9][0-9][-.]*.orig' + ) | LANG=C sort | LANG=C uniq > .gitignore + + git rm --cached -r . + git add --all . + git commit -m "*** clean state (ignored files removed)" + git tag -f CLEAN + + if [[ "$REVERT_COUNT" == "" || $REVERT_COUNT -gt 0 ]]; then + + # parse the list of patches + IFS=$'\n' read -rd '' -a PATCH_LIST <<<"$(sed -n 's/^Patch\([0-9]\+\):[ ]\+\(.*\)$/\1 \2/p' ../"$SPEC" | sort -n)" + + if [[ "$BUILD_TYPE" == "NetworkManager" ]]; then + if containsElement idx "123 rh1085015-applet-translations.patch" "${PATCH_LIST[@]}"; then + # for rhel-6, NetworkManager contains some patches that break the script. In this + # case, truncate the list of what we would normally revert. + PATCH_LIST=("${PATCH_LIST[@]:$((idx+1))}") + fi + fi + + # truncate the list of patches to revert/reapply + if [[ "$REVERT_COUNT" == "" || "$REVERT_COUNT" -gt ${#PATCH_LIST[@]} ]]; then + echo "revert all ${#PATCH_LIST[@]} patches" + else + echo "revert the last $REVERT_COUNT patches of ${#PATCH_LIST[@]}" + PATCH_LIST=("${PATCH_LIST[@]:$((${#PATCH_LIST[@]} - $REVERT_COUNT))}") + fi + + # split the list in index and patch file name + PATCH_LIST_N=() + for i in ${!PATCH_LIST[@]}; do + LAST_PATCH_N[$i]=$(echo "${PATCH_LIST[$i]}" | sed -n 's/^\([0-9]\+\) \+.*$/\1/p') + LAST_PATCH[$i]=$( echo "${PATCH_LIST[$i]}" | sed -n 's/^\([0-9]\+\) \+\(.*\)$/\2/p') + done + + # revert and patches in reverse order... + BASECOMMIT=("`git rev-parse HEAD`") + for j in "${!PATCH_LIST[@]}"; do + i=$((${#PATCH_LIST[@]} - $j - 1)) + echo "revert Patch${LAST_PATCH_N[$i]} \"${LAST_PATCH[$i]}\"..." + PNUM="$(spec_parse_patch_p "../$SPEC" "${LAST_PATCH_N[$i]}")" + patch -f --no-backup-if-mismatch -R "-p$PNUM" < "../${LAST_PATCH[$i]}" || ( + # error applying patch. Maybe we have a multi line patch... + + rm -f "../${LAST_PATCH[$i]}".makerepo-split.* + split_patch "../${LAST_PATCH[$i]}" ".makerepo-split." + + git reset --hard + git clean -fdx + for p in "../${LAST_PATCH[$i]}".makerepo-split.*; do + echo ">>> try split part $p for ${LAST_PATCH[$i]}" + patch --no-backup-if-mismatch -R "-p$PNUM" < "$p" || die "error reverting Patch${LAST_PATCH_N[$i]} ${LAST_PATCH[$i]}" + done + ) + git add --all . + git commit --allow-empty -a -m "<< revert Patch${LAST_PATCH_N[$i]} \"${LAST_PATCH[$i]}\"$(get_patch_origin "${LAST_PATCH[$i]}")" + BASECOMMIT=("`git rev-parse HEAD`" "${BASECOMMIT[@]}") + git tag -f REVERT"${LAST_PATCH_N[$i]}" + done + + # reapply the patches + for i in ${!PATCH_LIST[@]}; do + echo "reapply Patch${LAST_PATCH_N[$i]} \"${LAST_PATCH[$i]}\"..." + + # create an empty commit, indicating the commit before starting to reapply + BASECOMMIT_REVERT="${BASECOMMIT[$((i))]}" + COMMIT_MSG="$(git log -n1 --format='%s%n%n%b' "$BASECOMMIT_REVERT" | sed '1s/<< revert \(Patch.*"\)$/-- before reapplying \1/')" + git commit --allow-empty -m "$COMMIT_MSG" + git tag -f "BEFORE_PATCH${LAST_PATCH_N[$i]}" + git tag -f "LAST0" + + # first try git-am to preserve the commit message, otherwise just revert the last commit + if git am "../${LAST_PATCH[$i]}"; then + # The tree to the version before should be identical after reapplying the patch. + # Just to be sure, reset the commit. + git reset "${BASECOMMIT[$((i+1))]}" -- . + COMMIT_MSG="$(git log -n1 --format='%s%n%n%b' "$BASECOMMIT_REVERT" | sed '1s/<< revert \(Patch.*"\)$/-- after reapplying \1\n\ngit-am did not fully restore the previous state/')" + git commit -m "$COMMIT_MSG" || echo "NOTHING TO COMMIT" + else + git am --abort + git reset "${BASECOMMIT[$((i+1))]}" -- . + COMMIT_MSG="$(git log -n1 --format='%s%n%n%b' "$BASECOMMIT_REVERT" | sed '1s/<< revert \(Patch.*"\)$/>> reapply \1/')" + git commit --allow-empty -m "$COMMIT_MSG" + fi + git reset --hard HEAD + git clean -fdx + [[ x = "x$(git diff "${BASECOMMIT[$((i+1))]}" HEAD)" ]] || die "error reverting patch" + git tag -f PATCH"${LAST_PATCH_N[$i]}" + done + git tag -f LAST + fi + git checkout "$ORIG_HEAD" -- . + git checkout HEAD~ -- .gitignore + git reset + + git gc +popd + +if [[ $LOCAL != 0 ]]; then + rm -rf ./.makerepo.git/ + mv "$DIRNAME/.git" ./.makerepo.git/ + $FEDPKG $DIST local -- --noclean + mv ./.makerepo.git/ "$DIRNAME/.git" + pushd "$DIRNAME" + git checkout -- .gitignore + + # write git-ignore file... + git status --porcelain | sed 's/^...//' >> "../.git/$MAKEREPO_GIT_IGNORE_LAST" + popd +fi + +echo SUCCESS; diff --git a/contrib/scripts/NM-log b/contrib/scripts/NM-log new file mode 100755 index 00000000..85faea86 --- /dev/null +++ b/contrib/scripts/NM-log @@ -0,0 +1,85 @@ +#!/bin/bash + +# Util to pretty-print logfile of NetworkManager +# +# Unless setting NM_LOG_NO_COLOR it will colorize the output. +# Suppress coloring with: +# $ NM_LOG_NO_COLOR=1 NM-log ... +# +# If called without arguments, it either reads from stdin (if not +# connected to a terminal) or it shows the journal content. +# +# If called with first argument "j", it always shows the journal content. +# +# You can pass multiple filenames. + +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + NM_not_sourced=1 +else + unset NM_not_sourced +fi + +NM-show-journal() { + local since="$(systemctl show NetworkManager | sed -n 's/^ExecMainStartTimestamp=\(.*\) [A-Z0-9]\+$/\1/p')" + + if [[ "$since" == "" ]]; then + echo "error detecting NM. Is it running?" + systemctl status NetworkManager + else + journalctl -o short-precise --since "$since" -b 0 -u NetworkManager "$@" + fi +} + +NM-colorize() { + if [[ "$NM_LOG_NO_COLOR" == "" ]]; then + # poor man's coloring using grep. + # TODO: do it somehow better (and more efficient). + sed 's/\r$//' | \ + GREP_COLOR='01;31' grep -a --color=always '^\|^\(.* \)\?<\(warn> \|error>\) \[[0-9.]*\]' | \ + GREP_COLOR='01;33' grep -a --color=always '^\|^\(.* \)\?<info> \[[0-9.]*\]\( .*\<is starting\>.*$\)\?' | \ + GREP_COLOR='01;37' grep -a --color=always '^\|\<platform:\( (.*)\)\? signal: .*$' | \ + GREP_COLOR='01;34' grep -a --color=always '^\|\<platform\(-linux\)\?:\( (.*)\)\? link: \(add\|adding\|change\|setting\|deleting\|enslaving to master\|releasing \([0-9]\+ \)\?from master\)\>\|\<platform: routing-rule: \(adding or updating:\|delete \)\|\<platform:\( (.*)\)\? address: \(deleting\|adding or updating\) IPv. address:\? \|\<platform:\( (.*)\)\? \(route\|ip4-route\|ip6-route\|qdisc\|tfilter\): \([a-z]\+\|adding or updating\|new\[0x[0-9A-Za-z]*\]\) \|\<platform-linux: sysctl: setting ' | \ + GREP_COLOR='01;35' grep -a --color=always '^\|\<audit: .*$' | \ + GREP_COLOR='01;32' grep -a --color=always '^\|\<device (.*): state change: ' | + if [[ "$NM_LOG_GREP" != "" ]]; then + GREP_COLOR='01;36' grep -a --color=always "^\\|$NM_LOG_GREP" + else + /bin/cat - + fi + else + /bin/cat - + fi +} + +NM-log() { + local NM_LOG_GREP= + + while [[ $# -gt 0 ]]; do + if [[ "$1" == "-h" ]]; then + shift + NM_LOG_GREP="${NM_LOG_GREP+$NM_LOG_GREP\\|}\\<$1\\>" + shift + else + break + fi + done + + ( + if [ "$1" == "j" ]; then + shift + NM-show-journal "$@" + elif [ "$#" -eq 0 -a -t 0 ]; then + NM-show-journal + else + a="${1--}" + shift + /usr/bin/less -f "$a" "$@" + fi + ) | \ + NM_LOG_GREP="$NM_LOG_GREP" NM-colorize | \ + LESS=FRSXM less -f -R --shift=5 +} + +if [[ "$NM_not_sourced" != "" ]]; then + NM-log "$@" +fi diff --git a/contrib/scripts/anonymize-logs.py b/contrib/scripts/anonymize-logs.py new file mode 100755 index 00000000..36b82ed1 --- /dev/null +++ b/contrib/scripts/anonymize-logs.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 + +from textwrap import wrap +import subprocess +import ipaddress +import argparse +import os +import re + + +domains = [] + +hosts_sub = {} +host_next = 0 + +macs_sub = {} +mac_next = 0 + +ips_sub = {} +ip4_next = ipaddress.IPv4Address("0.0.0.0") +ip6_next = ipaddress.IPv6Address("ffff::") + + +def main(args): + must_autoreplace_hostnames = not args.show_hostnames + must_replace_hostnames = must_autoreplace_hostnames or args.domain or args.hostname + + init_hostnames_and_domains_sub(args) + + with open(args.log_file) as f: + for line in (line.strip() for line in f): + if must_replace_hostnames: + line = replace_hostnames(line, must_autoreplace_hostnames) + if not args.show_macs: + line = replace_macs(line) + if not args.show_public_ips or args.hide_private_ips: + line = replace_ips(line, args.show_public_ips, args.hide_private_ips) + + print(line) + + +def init_hostnames_and_domains_sub(args): + global domains + + if not args.show_hostnames: + domains.extend(["com", "org", "net", "gov", "es", "it"]) + + r = subprocess.run("hostname", capture_output=True) + if r.returncode == 0: + own_hostname = r.stdout.decode().strip() + add_host_sub(own_hostname, ".self") + + # domains and hostname passed explicitly are replaced even with --show-hostnames + domains.extend(d.strip(". ") for d in args.domain) + domains = "|".join(domains) + + for hostname in args.hostname: + add_host_sub(hostname) + + +def add_host_sub(hostname: str, suffix: str = ""): + global hosts_sub + global host_next + + # if it's a domain-like hostname (i.e example.com) adds .ext at the end + if suffix == "" and re.search(r"\.({})$".format(domains), hostname): + suffix = ".ext" + + if hostname not in hosts_sub: + hosts_sub[hostname] = "hostname{}{}".format(host_next, suffix) + host_next += 1 + + +def replace_hostnames(line: str, autodetect_from_logs: bool) -> str: + global hosts_sub + + # look for known log messages that show hostnames + if autodetect_from_logs: + match = re.search(r"get-hostname: \"(.*)\"", line) + if match: + add_host_sub(match.group(1)) + + match = re.search(r"set hostname to \"(.*)\"", line) + if match: + add_host_sub(match.group(1)) + + match = re.search( + r"hostname changed from (\(none\)|\".*\") to (\(none\)|\".*\")", line + ) + if match: + if match.group(1) != "(none)": + add_host_sub(match.group(1).strip('"')) + if match.group(2) != "(none)": + add_host_sub(match.group(2).strip('"')) + + # look for domain-like strings + if domains: + match = re.search(r"[\w\-\.]+?\.(" + domains + r")\b", line) + if match: + add_host_sub(match.group(0)) + + for orig, repl in hosts_sub.items(): + line = line.replace(orig, repl) + + return line + + +def replace_macs(line: str) -> str: + global macs_sub + global mac_next + + macs = re.findall(r"(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}", line) + + for mac in macs: + if mac not in macs_sub: + macs_sub[mac] = ":".join(wrap("{:012x}".format(mac_next), width=2)) + mac_next += 1 + + line = line.replace(mac, macs_sub[mac]) + + return line + + +def replace_ips(line: str, show_public: bool, hide_private: bool) -> str: + global ips_sub + global ip4_next + global ip6_next + + ips4 = re.findall(r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}", line) + ips6 = re.findall(r"(?:[0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}", line) + + for addr_str in ips4 + ips6: + try: + addr = ipaddress.ip_address(addr_str) + except: # not IP + continue + + if (addr.is_private and not hide_private) or (addr.is_global and show_public): + continue + + if addr.exploded not in ips_sub: + if type(addr) is ipaddress.IPv4Address: + ips_sub[addr.exploded] = str(ip4_next).replace("0.", "IP4.", 1) + ip4_next += 1 + else: + ips_sub[addr.exploded] = str(ip6_next).replace("ffff:", "IPv6:", 1) + ip6_next += 1 + + line = line.replace(addr_str, ips_sub[addr.exploded]) + + return line + + +if __name__ == "__main__": + args_parser = argparse.ArgumentParser( + prog=os.path.basename(__file__), + description="""Anonymize some data from NetworkManager logs. + +Note that it only covers some common stuff like MAC and IP addresses or +hostnames. Do not trust it and manually review that the log doesn't contain +sensitive data before sharing it. + +Changing IP address can make that problems related to routing are impossible to +analyze. Because of that, private IPs which are normally not sensitive are not +hidden by default, and if the problem is related to routing you might need to +use the --show-public-ips option""", + epilog="Options of the type --show-* disable masking that type of data.", + formatter_class=argparse.RawTextHelpFormatter, + ) + args_parser.add_argument("-H", "--show-hostnames", action="store_true") + args_parser.add_argument("-m", "--show-macs", action="store_true") + args_parser.add_argument("-g", "--show-public-ips", action="store_true") + args_parser.add_argument("-p", "--hide-private-ips", action="store_true") + args_parser.add_argument( + "-d", + "--domain", + action="append", + default=[], + help='additional domains to hide, like ".xyz", can be passed more than once', + ) + args_parser.add_argument( + "-n", + "--hostname", + action="append", + default=[], + help="additional hostnames to hide, can be passed more than once", + ) + args_parser.add_argument( + "log_file", nargs="?", default="/dev/stdin", help="Log file (by default, stdin)" + ) + + args = args_parser.parse_args() + main(args) diff --git a/contrib/scripts/btmodem.pl b/contrib/scripts/btmodem.pl new file mode 100755 index 00000000..feaa32e0 --- /dev/null +++ b/contrib/scripts/btmodem.pl @@ -0,0 +1,291 @@ +#!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0-or-later + +# Copyright (C) 2019 Red Hat, Inc. + +# $ perldoc btmodem.pl if you'd like to read the manual, poor you: + +=head1 NAME + +btmodem.pl - emulate a bluetooth DUN modem + +=head1 SYNOPSIS + +btmodem.pl [<hci>] [-- <pppd> ...] + +=head1 DESCRIPTION + +B<btmodem.pl> registers a Bluetooth DUN profile with Bluez, accepts incoming +connections and pretends there's modem there. + +It answers a basic subset of AT commands, sufficient making ModemManager +recognize it as a 3GPP capable modem registered to a network. + +Upon receiving the dial (ATD) command, it spawns C<pppd> so that +NetworkManager can establish a connection. + +=head1 OPTIONS + +=over 4 + +=item B<< <hci> >> + +Create a service on this particular HCI. + +Defaults to I<hci0>. + +=item B<< <pppd> >> + +Specifies extra arguments to be prepended before C<pppd> to the default +set of I<nodetach notty local logfd 2 nopersist>. + +Defaults to I<pppd noauth dump debug 172.31.82.1:172.31.82.2>. + +=back + +=cut + +use strict; +use warnings; + +use IO::Handle; +use Net::DBus; +use Net::DBus::Reactor; + +# Parse command line arguments +my $hci_name; +my @pppd = qw/pppd noauth dump debug 172.31.82.1:172.31.82.2/; +while (@ARGV) { + $_ = shift @ARGV; + if ($_ eq '--') { + @pppd = @ARGV; + last; + } else { + die "Extra argument: '$_'" if $hci_name; + $hci_name = $_; + } +}; +$hci_name ||= 'hci0'; + +sub modemu +{ + my $fh = shift; + + while (<$fh>) { + chomp; + + if (/^AT$/ or /^ATE0$/ or /^ATV1$/ or /^AT\+CMEE=1$/ or /^ATX4$/ or /^AT&C1$/ or /^ATZ$/) { + # Standard Hayes commands that are basically used to + # ensure the modem is in a known state. Accept them all. + print $fh "\r\n"; + print $fh "OK\r\n"; + + } elsif (/^AT\+CPIN\?$/) { + # PIN unlocked. Required. + print $fh "\r\n"; + print $fh "+CPIN:READY\r\n"; + print $fh "\r\n"; + print $fh "OK\r\n"; + + } elsif (/^AT\+COPS=0$/) { + # Select access technology (we just accept 0=automatic) + print $fh "\r\n"; + print $fh "OK\r\n"; + + } elsif (/^AT\+CGREG\?$/) { + # 3GPP Registration status. + print $fh "\r\n"; + print $fh "+CGREG: 0,1\r\n"; + print $fh "\r\n"; + print $fh "OK\r\n"; + + } elsif (/^AT\+CGDCONT=\?$/) { + # Get supported PDP contexts + print $fh "\r\n"; + print $fh "+CGDCONT: (1-10),(\"IP\"),,,(0-1),(0-1)\r\n"; + print $fh "+CGDCONT: (1-10),(\"IPV6\"),,,(0-1),(0-1)\r\n"; + print $fh "OK\r\n"; + + } elsif (/^AT\+CGACT=0,1$/) { + # Activate a PDP context + print $fh "\r\n"; + print $fh "OK\r\n"; + + } elsif (/^AT\+CGDCONT=1,"(.*)","(.*)"$/) { + # Set PDP context. We accept any. + print $fh "\r\n"; + print $fh "OK\r\n"; + + } elsif (/^ATD/) { + print $fh "\r\n"; + print $fh "CONNECT 28800000\r\n"; + + my $ppp = fork; + die "Can't fork: $!" unless defined $ppp; + if ($ppp == 0) { + close STDIN; + close STDOUT; + open STDIN, '<&', $fh or die "Can't dup pty to a pppd stdin: $!"; + open STDOUT, '>&', $fh or die "Can't dup pty to a pppd stdout: $!"; + close $fh; + exec @pppd, qw/nodetach notty local logfd 2 nopersist/; + die "Can't exec pppd: $!"; + } + waitpid $ppp, 0; + } else { + print $fh "\r\n"; + print $fh "ERROR\r\n"; + } + } +} + +my $bus = Net::DBus->system; + +$bus->get_connection->register_object_path("/", sub { + my $bus = shift; + my $call = shift; + + # We only support the NewConnection call + next unless $call->get_type eq &Net::DBus::Binding::Message::MESSAGE_TYPE_METHOD_CALL; + if ( $call->get_interface ne 'org.bluez.Profile1' + or $call->get_path ne '/' + or $call->get_member ne 'NewConnection' + or $call->get_signature ne 'oha{sv}') { + + $bus->send ($bus->make_error_message ( + replyto => $call, + name => ' org.freedesktop.DBus.Error.Failed', + description => "Forgive me caller for I don't know what to do")); + next; + } + + my ($path, $fd, $args) = $call->get_args_list; + open (my $fh, "+>&=", $fd) or die $!; + + my $pid = fork; + die unless defined $pid; + + if ($pid == 0) { + # This allows us to use buffered read for lines from ModemManager + # despite not ending with \n + IO::Handle->input_record_separator ("\r"); + $fh->autoflush (1); + $fh->blocking (1); + modemu ($fh); + exit 0; + die; + } + + $bus->send ($bus->make_method_return_message ($call)) + unless $call->get_no_reply; +}); + +my $bluez = $bus->get_service ('org.bluez'); +my $profile_manager = $bluez->get_object ('/org/bluez', 'org.bluez.ProfileManager1'); + +$profile_manager->RegisterProfile('/', '00001103-0000-1000-8000-00805f9b34fb', {}); + +Net::DBus::Reactor->main->run; + +=head1 SETTING UP BLUETOOTH + +In order for this script useful, you need to have two Bluetooth interfaces +paired together. It's somewhat easier if you've got two machines to test. + +The pairing can be done withing the C<bluetoothctl> shell. Launch it after +you started C<btmodem.pl>, so that the right profile UUIDs are discovered +by the client. These commands come in handy: + +=over + +=item [bluetooth]# B<default-agent> + +This makes C<bluetoothctl> ask for pairing PIN in the shell session. That is +useful if you're ssh-ing into a machine instead of using a desktop shell with +its own agent. Run this on both machines. + +=item [bluetooth]# B<discoverable on> + +Broadcast the server service. You don't need to run this on the client. + +=item [bluetooth]# B<scan on> + +Turn on discovery of the devices. You need to don't run this on the server. + +After you've turned the discovery on, wait for a minute or so for your +server to get discovered. + +=item [bluetooth]# B<devices> + +List the known devices, both those who've been discovered and those that have +been paired with. + +=item [bluetooth]# B<pair 00:AA:01:00:00:23> + +Initiate the pairing. Run it from the machine that has scanning enabled. +Assumes your server is C<00:AA:01:00:00:23> -- check your real address with the +C<devices> command. + +After a short while, you should see the pairing confirmation prompt on both machines. + +=item [bluetooth]# B<trust 00:AA:01:00:00:24> + +Allow incoming connections from C<00:AA:01:00:00:24>. Run this on the server. + +=item B<nmcli c add type bluetooth ifname '*' gsm.apn internet bluetooth.type dun bluetooth.bdaddr 00:AA:01:00:00:23> + +If everything went right, you can now connect. + +=back + +=head1 EXAMPLES + +=over + +=item B<btmodem.pl> + +Just emulate a DUN modem on I<hci0>, with the default PPP arguments. + +=item B<btmodem.pl hci666> + +Same as above, just on the I<hci666> interface. + +=item B<btmodem.pl -- unshare --net pppd 172.31.82.1:172.31.82.2> + +Avoid polluting the namespace with the modem end of PPP connection. + +=item B<btmodem.pl -- pppd 10.0.0.1:10.0.0.2> + +Override the C<pppd> parameters: no debug logging and different set of +addresses. + +=item B<btmodem.pl mymodem -- pppd 10.0.0.1:10.0.0.2> + +Same as above, with a modem name different from default. + +=back + +=head1 BUGS + +Haha. You tell me. + +=head1 SEE ALSO + +L<ModemManager(8)>, L<pppd(8)>, C<modemu.pl> + +=head1 COPYRIGHT + +Copyright (C) 2019 Lubomir Rintel + +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 2 of the License, or +(at your option) any later version. + +=head1 AUTHOR + +Lubomir Rintel C<lkundrak@v3.sk> + +Like, it's me who wrote it, but if you're running it it's your problem. + +=cut diff --git a/contrib/scripts/checkpatch-feature-branch.sh b/contrib/scripts/checkpatch-feature-branch.sh new file mode 100755 index 00000000..d6f72b20 --- /dev/null +++ b/contrib/scripts/checkpatch-feature-branch.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +die() { + printf "%s\n" "$@" + exit 1 +} + +HEAD="${1:-HEAD}" + +BASE_DIR="$(dirname "$0")" + +if printf '%s' "$HEAD" | grep -q '\.\.'; then + # Check the explicitly specified range from the argument. + REFS=( $(git log --reverse --format='%H' "$HEAD") ) || die "not a valid range (HEAD is $HEAD)" +else + BASE_REF="refs/remotes/origin" + NM_UPSTREAM_REMOTE= + + if [ "$NM_CHECKPATCH_FETCH_UPSTREAM" == 1 ]; then + NM_UPSTREAM_REMOTE="nm-upstream-$(date '+%Y%m%d-%H%M%S')-$RANDOM" + git remote add "$NM_UPSTREAM_REMOTE" https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git + BASE_REF="refs/remotes/$NM_UPSTREAM_REMOTE" + git fetch origin "$(git rev-parse "$HEAD")" --no-tags --unshallow + git fetch "$NM_UPSTREAM_REMOTE" \ + --no-tags \ + "refs/heads/main:$BASE_REF/main" \ + "refs/heads/nm-*:$BASE_REF/nm-*" \ + || die "failure to fetch from https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git" + fi + + # the argument is only a single ref (or the default "HEAD"). + # Find all commits that branch off one of the stable branches or main + # and lead to $HEAD. These are the commits of the feature branch. + + RANGES=( $(git show-ref | sed 's#^\(.*\) '"$BASE_REF/"'\(main\|nm-1-[0-9]\+\)$#\1..'"$HEAD"'#p' -n) ) + + [ "${#RANGES[@]}" != 0 ] || die "cannot detect git-ranges (HEAD is $(git rev-parse "$HEAD"))" + + REFS=( $(git log --reverse --format='%H' "${RANGES[@]}") ) + + if [ "${#REFS[@]}" == 0 ] ; then + # no refs detected. This means, $HEAD is already on main (or one of the + # stable nm-1-* branches. Just check the patch itself. + REFS=( "$HEAD" ) + fi + + if [ -n "$NM_UPSTREAM_REMOTE" ]; then + git remote remove "$NM_UPSTREAM_REMOTE" + fi +fi + +SUCCESS=0 +for H in "${REFS[@]}"; do + export NM_CHECKPATCH_HEADER=$'\n'">>> VALIDATE \"$(git log --oneline -n1 "$H")\"" + git format-patch -U65535 --stdout -1 "$H" | "$BASE_DIR/checkpatch.pl" + if [ $? != 0 ]; then + SUCCESS=1 + fi +done + +exit $SUCCESS diff --git a/contrib/scripts/checkpatch-git-post-commit-hook b/contrib/scripts/checkpatch-git-post-commit-hook new file mode 100755 index 00000000..96479107 --- /dev/null +++ b/contrib/scripts/checkpatch-git-post-commit-hook @@ -0,0 +1,23 @@ +#!/bin/sh + +# contrib/scripts/checkpatch-git-post-commit-hook: +# Call this script via ".git/hooks/post-commit" + +DISABLED=${NM_HOOK_DISABLED:0} + +if [ "$DISABLED" == 1 ]; then + echo "COMMIT HOOK DISABLED" + exit 0 +fi + +FILE=contrib/scripts/checkpatch-feature-branch.sh +if [ -x "$FILE" ]; then + "$FILE" + exit 0 +fi + +FILE=contrib/scripts/checkpatch.pl +if [ -x "$FILE" ]; then + git format-patch -U65535 --stdout -1 | "$FILE" + exit 0 +fi diff --git a/contrib/scripts/checkpatch.pl b/contrib/scripts/checkpatch.pl new file mode 100755 index 00000000..c24db35e --- /dev/null +++ b/contrib/scripts/checkpatch.pl @@ -0,0 +1,319 @@ +#!/usr/bin/perl -n +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2018,2021 Red Hat, Inc. +# + +# $ perldoc checkpatch.pl for eye-pleasing view of the manual: + +=head1 NAME + +checkpatch.pl - check for common mistakes + +=head1 SYNOPSIS + +checkpatch.pl [<file> ...] + +=head1 DESCRIPTION + +B<checkpatch.pl> checks source files or patches for common mistakes. + +=head1 OPTIONS + +=over 4 + +=item B<< <file> >> + +A C source file or an unified diff. + +=back + +=cut + +use strict; +use warnings; + +chomp; + +our $is_patch; +our $is_file; +our $is_commit_message; + +our $seen_error; +our $line; # Current line +our $check_line; # Complain if errors are found on this line + +our @functions_seen; +our $type; +our $filename; +our $line_no; +our $indent; +our $check_is_todo; +our $expect_spdx; +our $subdir; + +sub new_hunk +{ + $type = undef; + $indent = undef; +} + +sub new_file +{ + $expect_spdx = 0; + $check_is_todo = 1; + $filename = $subdir // ''; + $filename .= shift; + @functions_seen = (); +} + +my $header = $ENV{'NM_CHECKPATCH_HEADER'}; + +sub complain +{ + my $message = shift; + my $plain_message = shift; + + return unless $check_line; + + if (defined($header)) { + warn "$header\n"; + undef $header; + } + + if ($plain_message) { + warn "$message\n"; + } else { + warn "$filename:$line_no: $message:\n"; + warn "> $line\n\n"; + } + $seen_error = 1; +} + +sub check_commit +{ + my $commit = shift; + my $required = shift; + my $commit_id; + my $commit_message; + + if ($commit =~ /^([0-9a-f]{5,})\b/) { + $commit_id = $1; + } else { + return unless $required; + } + + if ($commit_id and not system 'git rev-parse --git-dir >/dev/null 2>/dev/null') { + $commit_message = `git log --abbrev=12 --pretty=format:"%h ('%s')" -1 "$commit_id" 2>/dev/null`; + complain "Commit '$commit_id' does not seem to exist" unless $commit_message; + } + + $commit_message //= "<12 hex digits> ('<commit subject>')"; + complain "Refer to the commit id properly: $commit_message" unless $commit =~ /^[0-9a-f]{12} \('/; +} + +if ($is_patch) { + # This is a line of an unified diff + if (/^@@.*\+(\d+)/) { + $line_no = $1 - 1; + new_hunk; + next; + } + if (/^\+\+\+ (b\/)?(.*)/) { + new_file ($2); + next; + } + s/^([ \+])(.*)/$2/ or next; + $line_no++; + $check_line = $1 eq '+'; + $line = $2; +} elsif ($is_file) { + $line_no = $.; + $. = 0 if eof; + # This is a line from full C file + $check_line = 1; + $line = $_; +} elsif ($is_commit_message) { + $line_no++; + $filename = '(commit message)'; + $check_line = 1; + $line = $_; + /^---$/ and $is_commit_message = 0; + /^(Reverts|Fixes): *(.*)/ and check_commit ($2, 1); + /This reverts commit/ and next; + /cherry picked from/ and next; + /^git-subtree-dir: (.*)/ and $subdir = "$1/"; + /\bcommit (.*)/ and check_commit ($1, 0); + next; +} else { + # We don't handle these yet + /^diff --cc/ and exit 0; + $filename = ''; + $line_no = 1; + # We don't know if we're dealing with a patch or a C file yet + $is_commit_message = 1 if /^From \S/; + $is_file = 1 if /^#/; + $is_patch = 1 if /^---/; + next; +} + +if ($is_file and $filename ne $ARGV) { + new_file ($ARGV); + new_hunk; +} + +if ($filename !~ /\.[ch]$/) { + if ($check_is_todo) { + complain("Resolve todo list \"$filename\" first\n", 1) if $filename =~ /^TODO.txt$/; + $check_is_todo = 0; + } + next; +} + +next if $filename =~ /\/nm-[^\/]+-enum-types\.[ch]$/; +next if $filename =~ /\b(shared|src)\/systemd\// + and not $filename =~ /\/sd-adapt\// + and not $filename =~ /\/nm-/; +next if $filename =~ /\/(n-acd|c-list|c-siphash|n-dhcp4)\//; + +$expect_spdx = 1 if $line_no == 1; +$expect_spdx = 0 if $line =~ /SPDX-License-Identifier/; +complain ('Missing a SPDX-License-Identifier') if $line_no == 2 and $expect_spdx; + +complain ('Tabs are only allowed at the beginning of a line') if $line =~ /[^\t]\t/; +complain ('Trailing whitespace') if $line =~ /[ \t]$/; +complain ('Don\'t use glib typedefs for char/short/int/long/float/double') if $line =~ /\bg(char|short|int|long|float|double)\b/; +complain ("Don't use \"$1 $2\" instead of \"$2 $1\"") if $line =~ /\b(char|short|int|long) +(unsigned|signed)\b/; +complain ("Don't use \"unsigned int\" but just use \"unsigned\"") if $line =~ /\b(unsigned) +(int)\b/; +complain ("Please use LGPL-2.1-or-later SPDX tag for new files") if $is_patch and $line =~ /SPDX-License-Identifier/ and not /LGPL-2.1-or-later/; +complain ("Use a SPDX-License-Identifier instead of Licensing boilerplate") if $is_patch and $line =~ /under the terms of/; +complain ("Don't use space inside elvis operator ?:") if $line =~ /\?[\t ]+:/; +complain ("Don't add Emacs editor formatting hints to source files") if $line_no == 1 and $line =~ /-\*-.+-\*-/; +complain ("XXX marker are reserved for development while work-in-progress. Use TODO or FIXME comment instead?") if $line =~ /\bXXX\b/; +complain ("This gtk-doc annotation looks wrong") if $line =~ /\*.*\( *(transfer-(none|container|full)|allow none) *\) *(:|\()/; +complain ("The gtk-doc annotation (allow-none) is deprecated. Use either (nullable) and/or (optional). See https://gi.readthedocs.io/en/latest/annotations/giannotations.html#deprecated-gobject-introspection-annotations") if $line =~ /\*.*\( *(allow-none) *\) *(:|\()/; +complain ("Prefer nm_assert() or g_return*() to g_assert*()") if $line =~ /g_assert/ and (not $filename =~ /\/tests\//) and (not $filename =~ /\/nm-test-/); +complain ("Use gs_free_error with GError variables") if $line =~ /\bgs_free\b +GError *\*/; +complain ("Initialize GError variables to NULL, if you pass them on") if $line =~ /\bGError +\*([a-z0-9_]+);/; +complain ("Don't use strcmp/g_strcmp0 unless you need to sort. Consider nm_streq()/nm_streq0(),NM_IN_STRSET() for testing equality") if $line =~ /\b(strcmp|g_strcmp0)\b/; +complain ("Don't use API that uses the numeric source id. Instead, use GSource and API like nm_g_idle_add(), nm_g_idle_add_source(), nm_clear_g_source_inst(), etc.") if $line =~ /\b(g_idle_add|g_idle_add_full|g_timeout_add|g_timeout_add_seconds|g_source_remove|nm_clear_g_source)\b/; +complain ("Prefer g_snprintf() over snprintf() (for consistency)") if $line =~ /\b(snprintf)\b/; +complain ("Prefer nm_str_hash()/nm_direct_hash() over g_str_hash()/g_direct_hash(). Those use siphash24") if $line =~ /\b(g_str_hash|g_direct_hash)\b/; +complain ("Don't use g_direct_equal() for hash tables, pass NULL for pointer equality which avoids the function call") if $line =~ /\b(g_direct_equal)\b/; +complain ("Prefer nm_pint_hash()/nm_pint64_hash()/nm_pdouble_hash() over g_int_hash()/g_int64_hash()/g_double_hash(). Those use siphash24") if $line =~ /\b(g_int_hash|g_int64_hash|g_double_hash)\b/; +complain ("Prefer nm_pint_equal()/nm_pint64_equal()/nm_pdouble_equal() over g_int_equal()/g_int64_equal()/g_double_equal(). Those names mirror our nm_p*_hash() functions") if $line =~ /\b(g_int_equal|g_int64_equal|g_double_equal)\b/; +complain ("Avoid g_clear_pointer() and use nm_clear_pointer() (or nm_clear_g_free(), g_clear_object(), etc.)") if $line =~ /\b(g_clear_pointer)\b/; +complain ("Define setting properties with _nm_setting_property_define_direct_*() API") if $line =~ /g_param_spec_/ and $filename =~ /\/libnm-core-impl\/nm-setting/; +complain ("Use nm_g_array_{index,first,last,index_p}() instead of g_array_index(), as it nm_assert()s for valid element size and out-of-bound access") if $line =~ /\bg_array_index\b/; +complain ("Use spaces instead of tabs") if $line =~ /\t/; +complain ("Prefer implementing private pointers via _NM_GET_PRIVATE() or _NM_GET_PRIVATE_PTR() (the latter, if the private data has an opqaue pointer in the header file)") if $line =~ /\b(g_type_class_add_private|G_TYPE_INSTANCE_GET_PRIVATE)\b/; +complain ("Don't use close()/g_close(). Instead, use nm_close() (or nm_close_with_error()).") if $line =~ /\b(close|g_close)\b *\(/; +complain ("Use nm_memdup() instead of g_memdup(). The latter has a size argument of type guint") if $line =~ /\bg_memdup\b/; + +# Further on we process stuff without comments. +$_ = $line; +s/\s*\/\*.*\*\///; +s/\s*\/\*.*//; +s/\s*\/\/.*//; +/^\s* \* / and next; + +if (/^typedef*/) { + # We expect the { on the same line as the typedef. Otherwise it + # looks too much like a function declaration + complain ('Unexpected line break following a typedef') unless /[;{,]$/; + next; +} elsif (/^[A-Za-z_][A-Za-z0-9_ ]*\*?$/ and /[a-z]/) { + # A function type + $type = $_; + next; +} elsif ($type and /^([A-Za-z_][A-Za-z0-9_]*)(\s*)\(/) { + my @order = qw/^get_property$ ^set_property$ (?<!_iface|_class)_init$ ^constructor$ + ^constructed$ _new$ ^dispose$ ^finalize$ _class_init$/; + my @following = (); + my @tmp = (); + + # A function name + my $name = $1; + complain ('No space between function name and arguments') unless $2 eq ''; + + # Determine which function must not be preceding this one + foreach my $func (reverse @order) { + if ($name =~ /$func/) { + @following = @tmp; + last; + } + push @tmp, $func; + } + + # Check if an out-of-order function was seen + foreach my $func (@following) { + my @wrong = grep { /$func/ } @functions_seen; + complain (join (', ', map { "'$_'" } @wrong)." should follow '$name'") if @wrong; + } + + push @functions_seen, $1; + $type = undef; + next; +} + +if ($type) { + # We've seen what looked like a type in a function declaration, + # but the function declaration didn't follow. + if ($type =~ /^(struct|union)/ and $line eq '{') { + complain ("Brace should be one the same line as the '$type' declaration"); + } else { + complain ("Expected a function declaration following '$type', but found something else"); + } + $type = undef; +} + +END { + if ($seen_error) { + warn "The patch does not validate.\n" if $is_patch; + warn "The file does not validate.\n" if $is_file; + $? = 1 + } +}; + +=head1 EXAMPLES + +=over + +=item B<checkpatch.pl hello.c> + +Check a single file. + +=item B<git diff --cached |checkpatch.pl> + +Check the currently staged changes. + +=item B<git format-patch -U65535 --stdout -1 |contrib/scripts/checkpatch.pl || :> + +A F<.git/hooks/post-commit> oneliner that, wisely, tolerates failures while +still providing advice. The large line context allows helps checkpatch.pl +get a better idea about the changes in context of code that does not change. + +=back + +=head1 BUGS + +Proabably too many. + +=head1 SEE ALSO + +F<CONTRIBUTING> + +=head1 COPYRIGHT + +Copyright (C) 2018,2021 Red Hat + +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 2 of the License, or +(at your option) any later version. + +=head1 AUTHOR + +Lubomir Rintel C<lkundrak@v3.sk> + +=cut diff --git a/contrib/scripts/code-style-git-post-commit-hook b/contrib/scripts/code-style-git-post-commit-hook new file mode 100755 index 00000000..8d464842 --- /dev/null +++ b/contrib/scripts/code-style-git-post-commit-hook @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +DISABLED=${NM_HOOK_DISABLED:0} + +if [ "$DISABLED" == 1 ]; then + echo "COMMIT HOOK DISABLED" + exit 0 +fi + +FORMATTER=contrib/scripts/nm-code-format.sh + +# Filter only C source files +CHANGED_FILES=$(git log --pretty='' --name-only -n1 | grep -E '\.c$|\.h$' | tr '\n' ' ') + +echo $CHANGED_FILES + +if [ -x "$FORMATTER" ] && [ ! -z "$CHANGED_FILES" ]; then + "$FORMATTER" -n "${CHANGED_FILES}" +fi diff --git a/contrib/scripts/find-backports b/contrib/scripts/find-backports new file mode 100755 index 00000000..c2082570 --- /dev/null +++ b/contrib/scripts/find-backports @@ -0,0 +1,417 @@ +#!/usr/bin/env python3 + +import subprocess +import collections +import os +import sys +import re +import pprint + + +FNULL = open(os.devnull, "w") +pp = pprint.PrettyPrinter(indent=4, stream=sys.stderr) + +DEBUG = os.environ.get("NM_FIND_BACKPORTS_DEBUG", None) == "1" + + +def dbg_log(s): + if DEBUG: + print(s, file=sys.stderr) + + +def dbg_pprint(obj): + if DEBUG: + pp.pprint(obj) + + +def print_err(s): + print(s, file=sys.stderr) + + +def die(s): + print_err(s) + sys.exit(1) + + +def memoize(f): + memo = {} + + def helper(x): + if x not in memo: + memo[x] = f(x) + return memo[x] + + return helper + + +def re_bin(r): + return r.encode("utf8") + + +def _keys_to_dict(itr): + d = collections.OrderedDict() + for c in itr: + d[c] = None + return d + + +@memoize +def git_ref_exists_full_path(ref): + val = git_ref_exists(ref) + if val: + try: + subprocess.check_output(["git", "show-ref", "-q", "--verify", str(ref)]) + except subprocess.CalledProcessError: + pass + else: + return val + return None + + +def _git_ref_exists_eval(ref): + try: + out = subprocess.check_output( + ["git", "rev-parse", "--verify", str(ref) + "^{commit}"], + stderr=FNULL, + ) + except subprocess.CalledProcessError: + return None + o = out.decode("ascii").strip() + if len(o) == 40: + return o + raise Exception(f"git-rev-parse for '{ref}' returned unexpected output {out}") + + +_git_ref_exists_cache = {} + + +def git_ref_exists(ref): + val = _git_ref_exists_cache.get(ref, False) + + if val is False: + val = _git_ref_exists_eval(ref) + _git_ref_exists_cache[ref] = val + if val and ref != val: + _git_ref_exists_cache[val] = val + + return val + + +@memoize +def git_get_head_name(ref): + out = subprocess.check_output( + ["git", "rev-parse", "--symbolic-full-name", str(ref)], stderr=FNULL + ) + return out.decode("utf-8").strip() + + +def git_merge_base(a, b): + out = subprocess.check_output(["git", "merge-base", str(a), str(b)], stderr=FNULL) + out = out.decode("ascii").strip() + assert git_ref_exists(out) + return out + + +def git_all_commits_grep(rnge, grep=None): + if grep: + grep = [("--grep=%s" % g) for g in grep] + notes = ["-c", "notes.displayref=refs/notes/bugs"] + else: + grep = [] + notes = [] + out = subprocess.check_output( + ["git"] + + notes + + ["log", "--pretty=%H", "--notes", "--reverse"] + + grep + + [str(rnge)], + stderr=FNULL, + ) + return [x for x in out.decode("ascii").split("\n") if x] + + +def git_logg(commits): + commits = list(commits) + if not commits: + return "" + out = subprocess.check_output( + [ + "git", + "log", + "--no-show-signature", + "--no-walk", + "--pretty=format:%Cred%h%Creset - %Cgreen(%ci)%Creset [%C(yellow)%an%Creset] %s%C(yellow)%d%Creset", + "--abbrev-commit", + "--date=local", + ] + + [str(c) for c in commits], + stderr=FNULL, + ) + return out.decode("utf-8").strip() + + +@memoize +def git_all_commits(rnge): + return git_all_commits_grep(rnge) + + +@memoize +def git_all_commits_set(rnge): + return set(git_all_commits_grep(rnge)) + + +def git_commit_sorted(commits): + commits = list(commits) + if not commits: + return [] + out = subprocess.check_output( + ["git", "log", "--no-walk", "--pretty=%H", "--reverse"] + + [str(x) for x in commits], + stderr=FNULL, + ) + out = out.decode("ascii") + return [x for x in out.split("\n") if x] + + +@memoize +def git_ref_commit_body(ref): + return subprocess.check_output( + [ + "git", + "-c", + "notes.displayref=refs/notes/bugs", + "log", + "-n1", + "--pretty=%B%n%N", + str(ref), + ], + stderr=FNULL, + ) + + +@memoize +def git_ref_commit_body_get_fixes(ref): + body = git_ref_commit_body(ref) + result = [] + for mo in re.finditer(re_bin("\\b[fF]ixes: *([0-9a-z]+)\\b"), body): + c = mo.group(1).decode("ascii") + h = git_ref_exists(c) + if h: + result.append(h) + if result: + # The commit that contains a "Fixes:" line, can also contain an "Ignore-Fixes:" line + # to disable it. This only makes sense with refs/notes/bugs notes, to fix up a wrong + # annotation. + for mo in re.finditer(re_bin("\\bIgnore-[fF]ixes: *([0-9a-z]+)\\b"), body): + c = mo.group(1).decode("ascii") + h = git_ref_exists(c) + try: + result.remove(h) + except ValueError: + pass + + return result + + +@memoize +def git_ref_commit_body_get_cherry_picked_one(ref): + ref = git_ref_exists(ref) + if not ref: + return None + body = git_ref_commit_body(ref) + result = None + for r in [ + re_bin("\\(cherry picked from commit ([0-9a-z]+)\\)"), + re_bin("\\bIgnore-Backport: *([0-9a-z]+)\\b"), + ]: + for mo in re.finditer(r, body): + c = mo.group(1).decode("ascii") + h = git_ref_exists(c) + if h: + if not result: + result = [h] + else: + result.append(h) + return result + + +@memoize +def git_ref_commit_body_get_cherry_picked_recurse(ref): + ref = git_ref_exists(ref) + if not ref: + return None + + def do_recurse(result, ref): + result2 = git_ref_commit_body_get_cherry_picked_one(ref) + if result2: + extra = [h2 for h2 in result2 if h2 not in result] + if extra: + result.extend(extra) + for h2 in extra: + do_recurse(result, h2) + + result = [] + do_recurse(result, ref) + return result + + +def git_commits_annotate_fixes(rnge): + commits = git_all_commits(rnge) + c_dict = _keys_to_dict(commits) + for c in git_all_commits_grep(rnge, grep=["[Ff]ixes:"]): + ff = git_ref_commit_body_get_fixes(c) + if ff: + c_dict[c] = ff + return c_dict + + +def git_commits_annotate_cherry_picked(rnge): + commits = git_all_commits(rnge) + c_dict = _keys_to_dict(commits) + for c in git_all_commits_grep( + ref_head, grep=["cherry picked from commit", "Ignore-Backport:"] + ): + ff = git_ref_commit_body_get_cherry_picked_recurse(c) + if ff: + c_dict[c] = ff + return c_dict + + +def git_ref_in_history(ref, rnge): + return git_ref_exists(ref) in git_all_commits_set(rnge) + + +if __name__ == "__main__": + if len(sys.argv) <= 1: + ref_head0 = "HEAD" + else: + ref_head0 = sys.argv[1] + + ref_head = git_ref_exists(ref_head0) + if not ref_head: + die('Ref "%s" does not exist' % (ref_head0)) + + if not git_ref_exists_full_path("refs/notes/bugs"): + die( + "Notes refs/notes/bugs not found. Read CONTRIBUTING.md file for how to setup the notes" + ) + + ref_upstreams = [] + if len(sys.argv) <= 2: + head_name = git_get_head_name(ref_head0) + match = False + if head_name: + match = re.match("^refs/(heads|remotes/[^/]*)/nm-1-([0-9]+)$", head_name) + if match: + i = int(match.group(2)) + while True: + i += 2 + r = "nm-1-" + str(i) + if not git_ref_exists(r): + r = "refs/remotes/origin/nm-1-" + str(i) + if not git_ref_exists(r): + break + ref_upstreams.append(r) + ref_upstreams.append("main") + + if not ref_upstreams: + if len(sys.argv) <= 2: + ref_upstreams = ["main"] + else: + ref_upstreams = list(sys.argv[2:]) + + for h in ref_upstreams: + if not git_ref_exists(h): + die('Upstream ref "%s" does not exist' % (h)) + + print_err("Check %s (%s)" % (ref_head0, ref_head)) + print_err("Upstream refs: %s" % (ref_upstreams)) + + print_err('Check patches of "%s"...' % (ref_head)) + own_commits_list = git_all_commits(ref_head) + own_commits_cherry_picked = git_commits_annotate_cherry_picked(ref_head) + + cherry_picks_all = collections.OrderedDict() + for c, cherry_picked in own_commits_cherry_picked.items(): + if cherry_picked: + for c2 in cherry_picked: + l = cherry_picks_all.get(c2) + if not l: + cherry_picks_all[c2] = [c] + else: + l.append(c) + + own_commits_cherry_picked_flat = set() + for c, p in own_commits_cherry_picked.items(): + own_commits_cherry_picked_flat.add(c) + if p: + own_commits_cherry_picked_flat.update(p) + + dbg_log(">>> own_commits_cherry_picked") + dbg_pprint(own_commits_cherry_picked) + + dbg_log(">>> cherry_picks_all") + dbg_pprint(cherry_picks_all) + + # find all commits on the upstream branches that fix another commit. + fixing_commits = {} + for ref_upstream in ref_upstreams: + ref_str = ref_head + ".." + ref_upstream + print_err(f'Check upstream patches "{ref_str}"...') + for c, fixes in git_commits_annotate_fixes(ref_str).items(): + if not fixes: + dbg_log(f">>> test {c} : SKIP (does not fix anything)") + continue + if c in cherry_picks_all: + # commit 'c' is already backported. Skip it. + dbg_log(f">>> test {c} => {fixes} : SKIP (already backported)") + continue + dbg_log(f">>> test {c} => {fixes} : process") + for f in fixes: + if f not in own_commits_cherry_picked_flat: + # commit "c" fixes commit "f", but this is not one of our own commits + # and not interesting. + dbg_log(f">>> fixes {f} not in own_commits_cherry_picked") + continue + dbg_log(f">>> take {c} (fixes {fixes})") + fixing_commits[c] = fixes + break + + extra = collections.OrderedDict( + [(c, git_ref_commit_body_get_cherry_picked_recurse(c)) for c in fixing_commits] + ) + extra2 = [] + for c in extra: + is_back = False + for e_v in extra.values(): + if c in e_v: + is_back = True + break + if not is_back: + extra2.append(c) + + commits_good = extra2 + + commits_good = git_commit_sorted(commits_good) + + print_err(git_logg(commits_good)) + + not_in = [ + c + for c in commits_good + if not git_ref_in_history(c, f"{ref_head}..{ref_upstreams[0]}") + ] + if not_in: + print_err("") + print_err( + f'WARNING: The following commits are not from the first reference "{ref_upstreams[0]}".' + ) + print_err( + f' You may want to first backports those patches to "{ref_upstreams[0]}".' + ) + for l in git_logg(git_commit_sorted(not_in)).splitlines(): + print_err(f" - {l}") + print_err("") + + for c in reversed(commits_good): + print("%s" % (c)) diff --git a/contrib/scripts/git-backport-merge b/contrib/scripts/git-backport-merge new file mode 100755 index 00000000..8ba2244f --- /dev/null +++ b/contrib/scripts/git-backport-merge @@ -0,0 +1,58 @@ +#!/bin/bash + +# Uses `git cherry-pick -x` to backport a merge commit to an older branch. +# +# Usage: +# First checkout the old-stable branch, that is the target for the backport. +# Then `git-backport-merge MERGE_REF [REFS...]` +# MERGE_REF is the merge commit that should be backported. +# [REFS...] is the commits that should be backported. If omitted, +# it automatically takes the parent commits of the merge commit. + +die() { + printf '%s\n' "$*" >&2 + exit 1 +} + +backport_merge() { + test "$#" -gt 0 || die "Requires the commit ref to backport (and optimally select the commits to include)" + + local M="${@:$#}" + local h + + if test "$#" -eq 1; then + local C=($(git log --reverse "--pretty=%H" "$M"^1.."$M"^2)) + else + local C=("${@:1:$#-1}") + fi + + local OLD_HEAD="$(git rev-parse HEAD)" || die "failed to get current HEAD" + + test -n "$(git status --porcelain --untracked-files=no)" && die "Working directory contains changes. Abort." + + local M_ID="$(git rev-parse "$M"^{commit})" || die "\"$M\" is not a valid commit" + + trap EXIT 'test -z "$OLD_HEAD" || git reset "$OLD_HEAD" --hard' + + for h in "${C[@]}"; do + if ! git cherry-pick --allow-empty -x "$h" ; then + git cherry-pick --abort + die "failed to cherry-pick commit \"$h\" on top of \"$(git rev-parse HEAD)\"" + fi + done + + local NEW_HEAD="$(git rev-parse HEAD)" || die "failed to get new HEAD" + + git reset --hard "$OLD_HEAD" || die "Failed to reset to previous HEAD \"$OLD_HEAD\"" + + git merge --no-ff --no-edit "$NEW_HEAD" || die "Failed to merge old HEAD \"$OLD_HEAD\" with new \"$NEW_HEAD\"" + + git commit --amend --allow-empty -C "$M" || die "Failed to amend merge commit \"$(git rev-parse HEAD)\" with commit message from \"$M\"" + + git rev-parse "$M" | sed 's/.*/(cherry picked from commit \0)/' | GIT_EDITOR='sh -c "cat >> \"$1\""' git commit --allow-empty --amend || \ + die "Failed to amend merge commit \"$(git rev-parse HEAD)\" with cherry-picked-from message from \"$M\"" + + OLD_HEAD= +} + +backport_merge "$@" diff --git a/contrib/scripts/git-subtree-reimport.sh b/contrib/scripts/git-subtree-reimport.sh new file mode 100755 index 00000000..73319044 --- /dev/null +++ b/contrib/scripts/git-subtree-reimport.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# In our git repository we vendor in several external projects. +# We do so via git-subtree. +# +# Run this script (without arguments) for re-importing the latest +# version of those projects. +# +# You can also specify the projects to reimport on the command line, +# ./contrib/scripts/git-subtree-reimport.sh [ c-list | c-rbtree | c-siphash | c-stdaux | n-acd | n-dhcp4 ... ] + +set -e + +cd "$(dirname "$(readlink -f "$0")")/../.." + +reimport() { + local d="$1" + local project + local branch + + if [[ "$d" = c-* ]] ; then + project=c-util + branch=main + else + project=nettools + branch=master + fi + + CMD=( git subtree pull --prefix "src/$d" "git@github.com:$project/$d.git" "$branch" --squash -m \ +"$d: re-import git-subtree for 'src/$d' + + git subtree pull --prefix src/$d git@github.com:$project/$d.git $branch --squash +" ) + + printf '\n>>>> %s >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n' "$d" + printf '>>>' + for c in "${CMD[@]}"; do + printf ' %q' "$c" + done + printf '\n' + + "${CMD[@]}" 2>&1 + + local REMOTE_COMMIT="$(git rev-parse FETCH_HEAD)" + + echo ">>>>> RESULT:" + printf ">>> git diff %s: HEAD:src/%s\n" "$REMOTE_COMMIT" "$d" + GIT_PAGER=cat git diff --color=always "$REMOTE_COMMIT:" "HEAD:src/$d" +} + +reimport_all() { + local ARGS + + ARGS=( "$@" ) + if [ "${#ARGS[@]}" = 0 ]; then + ARGS=( c-list c-rbtree c-siphash c-stdaux n-acd n-dhcp4 ) + fi + for d in "${ARGS[@]}" ; do + reimport "$d" + done +} + +reimport_all "$@" diff --git a/contrib/scripts/modemu.pl b/contrib/scripts/modemu.pl new file mode 100755 index 00000000..0e90fafa --- /dev/null +++ b/contrib/scripts/modemu.pl @@ -0,0 +1,299 @@ +#!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2018 Red Hat, Inc. +# + +# $ perldoc modemu.pl for eye-pleasing view of the manual: + +=head1 NAME + +modemu.pl - emulate a serial modem + +=head1 SYNOPSIS + +modemu.pl [<name>] [-- <pppd> ...] + +=head1 DESCRIPTION + +B<modemu.pl> opens a PTY, links the slave side to F</dev> and announces a +fake kobject via netlink as if it were a real serial device, so that +ModemManager picks it up. + +Then it answers to a very basic subset of AT commands, sufficient making +ModemManager recognize it as a 3GPP capable modem registered to a network. + +Upon receiving the dial (ATD) command, it spawns C<pppd> so that +NetworkManager can establish a connection. + +B<modemu.pl> needs superuser privileges to be able to announce a kobject +and create a F</dev> node. + +=head1 OPTIONS + +=over 4 + +=item B<< <name> >> + +Create a modem of given name. Links it to F<< /dev/<name> >>. + +Defaults to I<modemu>. + +=item B<< <pppd> >> + +Specifies extra arguments to be prepended before C<pppd> to the default +set of I<nodetach notty local logfd 2 nopersist>. + +Defaults to I<pppd dump debug 172.31.82.1:172.31.82.2>. + +=back + +=cut + +use strict; +use warnings; + +use Errno; +use Socket; +use IO::Pty; +use IO::Handle; + +use constant AF_NETLINK => 16; +use constant NETLINK_KOBJECT_UEVENT => 15; + +# This allows us to use buffered read for lines from ModemManager +# despite not ending with \n +IO::Handle->input_record_separator ("\r"); + +# Parse command line arguments +my $name; +my @pppd = qw/pppd dump debug 172.31.82.1:172.31.82.2/; +while (@ARGV) { + $_ = shift @ARGV; + if ($_ eq '--') { + @pppd = @ARGV; + last; + } else { + die "Extra argument: '$_'" if $name; + $name = $_; + } +}; +$name ||= 'modemu'; + +socket my $fd, AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT + or die "Can't create a netlink socket: $!"; + +my $seqnum = 666; +sub send_netlink +{ + my %props = (@_, SEQNUM => $seqnum++); + my $props = join '', map { $_, '=', $props{$_}, "\0" } keys %props; + + my $head = pack 'a8NLLLNLLL', + # signature + magic + 'libudev', + 0xfeedcafe, + + # 40 octets is the length of this header + 40, 40, 40 + length ($props), + + # Digest::MurmurHash2::Neutral::murmur_hash2_neutral("tty") + 0x8afa90c8, + + 0x00000000, + 0x00040002, + 0x00008010; + + $! = undef; + send $fd, "$head$props", 0, pack 'SSLL', AF_NETLINK, 0, 0, 0x0002; + # RHEL 7 kernel responds ECONNREFUSED even thoguh the sendto succeeded. Weird. + die "Can't send a netlink message: $!" if $! and not $!{ECONNREFUSED}; +} + +my $devpath = '/devices/pci0000:00/0000:00:00.0'; +unless (-d "/sys/$devpath") { + # Create a virtual device. Older ModemManager likes a hotpluggable bus + # (USB, PCI), but there's none on an IBM POWER lpar... + warn "No PCI bus to use for parent. Don't expect this to work with ModemManager 1.6"; + $devpath = '/devices/virtual'; +} + +my %props = ( + DEVPATH => "$devpath/$name", + SUBSYSTEM => 'tty', + DEVNAME => "/dev/$name", + + # Whitelisting that works for both ModemManager 1.6 and 1.8 + ID_MM_CANDIDATE => '1', + ID_MM_DEVICE_PROCESS => '1', +); + +sub cleanup +{ + unlink "/dev/$name"; + send_netlink (ACTION => 'remove', %props) if $fd; +} + +# Ensure we clean up before and after. +END { cleanup }; +$SIG{INT} = sub { cleanup; die }; +$SIG{TERM} = sub { cleanup; die }; +cleanup; + +my $pty = new IO::Pty; +my $ptyname = ttyname $pty; +symlink $ptyname, "/dev/$name" or die "Can't create /dev/$name: $!"; +send_netlink (ACTION => 'add', %props); +my ($pdptype, $apn); + +# Here's a good refernce of AT command a modern-ish modem probably uses: +# https://infocenter.nordicsemi.com/index.jsp?topic=%2Fref_at_commands%2FREF%2Fat_commands%2Fpacket_domain%2Fcgact_set.html + +while (<$pty>) { + chomp; + + if (/^AT$/ or /^ATE0$/ or /^ATV1$/ or /^AT\+CMEE=1$/ or /^ATX4$/ or /^AT&C1$/ or /^ATZ$/) { + # Standard Hayes commands that are basically used to + # ensure the modem is in a known state. Accept them all. + print $pty "\r\n"; + print $pty "OK\r\n"; + + } elsif (/^AT\+CPIN\?$/) { + # PIN unlocked. Required. + print $pty "\r\n"; + print $pty "+CPIN:READY\r\n"; + print $pty "\r\n"; + print $pty "OK\r\n"; + + } elsif (/^AT\+COPS=0$/) { + # Select access technology (we just accept 0=automatic) + print $pty "\r\n"; + print $pty "OK\r\n"; + + } elsif (/^AT\+CGREG\?$/) { + # 3GPP Registration status. + print $pty "\r\n"; + print $pty "+CGREG: 0,1\r\n"; + print $pty "\r\n"; + print $pty "OK\r\n"; + + # The PDP (packet data protocol/profile?) context handling below is very + # rudimentary: just enough to keep ModemManager 1.18 happy. It basically + # just starts with no contexts at all and then expects MM to set and + # activate profile number 1. + + } elsif (/^AT\+CGDCONT=\?$/) { + # Get supported PDP contexts + print $pty "\r\n"; + print $pty "+CGDCONT: (1-10),(\"IP\"),,,(0-1),(0-1)\r\n"; + print $pty "+CGDCONT: (1-10),(\"IPV6\"),,,(0-1),(0-1)\r\n"; + print $pty "OK\r\n"; + + } elsif (/^AT\+CGDCONT=1,"(.*)","(.*)"$/) { + # Create the PDP context. Remember it, MM is going to check it later + ($pdptype, $apn) = ($1, $2); + print $pty "\r\n"; + print $pty "OK\r\n"; + + + } elsif (/^AT\+CGDCONT\?$/) { + # List the PDP context we're aware of. + print $pty "\r\n"; + print $pty "+CGDCONT: 1,\"$pdptype\",\"$apn\",\"0.0.0.0\",0,0,0,0\r\n" + if defined $pdptype; + print $pty "OK\r\n"; + + } elsif (/^AT\+CGACT\?$/) { + # List available PDP contexts with states: profile 1 state 0 (inactive) + print $pty "\r\n"; + print $pty "+CGACT: 0,1\r\n"; + print $pty "OK\r\n"; + + } elsif (/^AT\+CGACT=0,1$/) { + # Deactivate a PDP context + print $pty "\r\n"; + print $pty "OK\r\n"; + + } elsif (/^AT\+COPS\?$/) { + # Current operators + # Not strictly required, but allows NetworkManager to just connect + # the modem device without explicitly setting an APN + print $pty "\r\n"; + print $pty "+COPS: 0,2,\"65302\",7\r\n"; # MCCMNC + print $pty "OK\r\n"; + + } elsif (/^ATD/) { + print $pty "\r\n"; + print $pty "CONNECT 28800000\r\n"; + + my $ppp = fork; + die "Can't fork: $!" unless defined $ppp; + if ($ppp == 0) { + close STDIN; + close STDOUT; + open STDIN, '<&', $pty or die "Can't dup pty to a pppd stdin: $!"; + open STDOUT, '>&', $pty or die "Can't dup pty to a pppd stdout: $!"; + close $pty; + exec @pppd, qw/nodetach notty local logfd 2 nopersist/; + die "Can't exec pppd: $!"; + } + waitpid $ppp, 0; + } else { + print $pty "\r\n"; + print $pty "ERROR\r\n"; + } +} + +=head1 EXAMPLES + +=over + +=item B<modemu.pl> + +Just create a modem named I<modemu>, with the default PPP arguments. + +=item B<modemu.pl ttyS666> + +Same as above, just name the modem I<ttyS666>. + +=item B<modemu.pl -- unshare --net pppd 172.31.82.1:172.31.82.2> + +Avoid polluting the namespace with the modem end of PPP connection. + +=item B<modemu.pl -- pppd 10.0.0.1:10.0.0.2> + +Override the C<pppd> parameters: no debug logging and different set of +addresses. + +=item B<modemu.pl mymodem -- pppd 10.0.0.1:10.0.0.2> + +Same as above, with a modem name different from default. + +=back + +=head1 BUGS + +Only works on machines with a PCI bus. ModemManager is picky about platform +devices and accepts PCI and USB buses easily. Which is why pretent to have +our tty on the PCI root device. + +Terminates after a single PPP session. C<pppd> seems to hang up the PTY. + +=head1 SEE ALSO + +L<ModemManager(8)>, L<pppd(8)> + +=head1 COPYRIGHT + +Copyright (C) 2018 Lubomir Rintel + +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 2 of the License, or +(at your option) any later version. + +=head1 AUTHOR + +Lubomir Rintel C<lkundrak@v3.sk> + +=cut diff --git a/contrib/scripts/nm-ci-patch-gtkdoc.sh b/contrib/scripts/nm-ci-patch-gtkdoc.sh new file mode 100755 index 00000000..e72a0a8f --- /dev/null +++ b/contrib/scripts/nm-ci-patch-gtkdoc.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# patch gtk-doc for https://gitlab.gnome.org/GNOME/gtk-doc/merge_requests/2 + +cd / + +patch -f -p 1 --fuzz 0 --reject-file=- <<EOF +diff --git a/usr/share/gtk-doc/python/gtkdoc/scan.py b/usr/share/gtk-doc/python/gtkdoc/scan.py +index f1f167235ab2e4c62676fbcfb87ebbe55c95b944..b59dd17abfa5f42b7bb06d239f9c78e5efffbf5d 100644 +--- a/usr/share/gtk-doc/python/gtkdoc/scan.py ++++ b/usr/share/gtk-doc/python/gtkdoc/scan.py +@@ -427,20 +427,26 @@ def ScanHeader(input_file, section_list, decl_list, get_types, options): + elif m9: + # We've found a 'typedef struct _<name> <name>;' + # This could be an opaque data structure, so we output an + # empty declaration. If the structure is actually found that + # will override this. + structsym = m9.group(1).upper() + logging.info('%s typedef: "%s"', structsym, m9.group(2)) + forward_decls[m9.group(2)] = '<%s>\n<NAME>%s</NAME>\n%s</%s>\n' % ( + structsym, m9.group(2), deprecated, structsym) + ++ bm = re.search(r'^(\S+)(Class|Iface|Interface)\b', m9.group(2)) ++ if bm: ++ objectname = bm.group(1) ++ logging.info('Found object: "%s"', objectname) ++ title = '<TITLE>%s</TITLE>' % objectname ++ + elif re.search(r'^\s*(?:struct|union)\s+_(\w+)\s*;', line): + # Skip private structs/unions. + logging.info('private struct/union') + + elif m10: + # Do a similar thing for normal structs as for typedefs above. + # But we output the declaration as well in this case, so we + # can differentiate it from a typedef. + structsym = m10.group(1).upper() + logging.info('%s:%s', structsym, m10.group(2)) +EOF + diff --git a/contrib/scripts/nm-ci-run.sh b/contrib/scripts/nm-ci-run.sh new file mode 100755 index 00000000..e385ef1e --- /dev/null +++ b/contrib/scripts/nm-ci-run.sh @@ -0,0 +1,299 @@ +#!/bin/bash + +# Arguments via environment variables: +# - CI +# - CC +# - BUILD_TYPE +# - CFLAGS +# - WITH_DOCS + +set -ex + +die() { + printf "%s\n" "$@" + exit 1 +} + +_is_true() { + case "$1" in + 1|y|yes|YES|Yes|on) + return 0 + ;; + 0|n|no|NO|No|off) + return 1 + ;; + "") + if [ "$2" == "" ]; then + die "not a boolean argument \"$1\"" + fi + _is_true "$2" + return $? + ;; + *) + die "not a boolean argument \"$1\"" + ;; + esac +} + +USE_CCACHE=0 +if command -v ccache &>/dev/null; then + USE_CCACHE=1 + export PATH="/usr/lib64/ccache:/usr/lib/ccache${PATH:+:${PATH}}" +fi + +IS_FEDORA=0 +IS_CENTOS=0 +IS_ALPINE=0 +grep -q '^NAME=.*\(CentOS\)' /etc/os-release && IS_CENTOS=1 +grep -q '^NAME=.*\(Fedora\)' /etc/os-release && IS_FEDORA=1 +grep -q '^NAME=.*\(Alpine\)' /etc/os-release && IS_ALPINE=1 + +############################################################################### + +if [ "$BUILD_TYPE" == meson ]; then + _TRUE=true + _FALSE=false +elif [ "$BUILD_TYPE" == autotools ]; then + _TRUE=yes + _FALSE=no +else + die "invalid \$BUILD_TYPE \"$BUILD_TYPE\"" +fi + +_WITH_CRYPTO="gnutls" +_WITH_WERROR=1 +_WITH_LIBTEAM="$_TRUE" +_WITH_DOCS="$_TRUE" +_WITH_SYSTEMD_LOGIND="$_TRUE" +if [ $IS_ALPINE = 1 ]; then + _WITH_SYSTEMD_LOGIND="$_FALSE" +fi + +if [ -z "${NMTST_SEED_RAND+x}" ]; then + NMTST_SEED_RAND="$SRANDOM" + if [ -z "$NMTST_SEED_RAND" ]; then + NMTST_SEED_RAND="$(( ( (RANDOM<<15|RANDOM)<<15|RANDOM ) % 0xfffffffe ))" + fi +fi +export NMTST_SEED_RAND + +case "$CI" in + ""|"true"|"default"|"gitlab") + CI=default + ;; + *) + die "invalid \$CI \"$CI\"" + ;; +esac + +if [ "$CC" != gcc ]; then + _WITH_CRYPTO=nss +fi + +if [ "$WITH_DOCS" != "" ]; then + if _is_true "$WITH_DOCS"; then + _WITH_DOCS="$_TRUE" + else + _WITH_DOCS="$_FALSE" + fi +fi + +unset _WITH_VALGRIND_CHECKED +_with_valgrind() { + _is_true "$WITH_VALGRIND" 0 || return 1 + + test "$_WITH_VALGRIND_CHECKED" = "1" && return 0 + _WITH_VALGRIND_CHECKED=1 + + if [ "$IS_ALPINE" = 1 ]; then + # on Alpine we have no debug symbols and the suppressions + # don't work. Skip valgrind tests. + WITH_VALGRIND=0 + fi + + # Certain glib2 versions are known to report *lots* of leaks. Disable + # valgrind tests in this case. + # https://bugzilla.redhat.com/show_bug.cgi?id=1710417 + if grep -q '^PRETTY_NAME="Fedora 30 (.*)"$' /etc/os-release ; then + if rpm -q glib2 | grep -q glib2-2.60.2-1.fc30 ; then + WITH_VALGRIND=0 + fi + elif grep -q '^PRETTY_NAME="Fedora 31 (.*)"$' /etc/os-release; then + if rpm -q glib2 | grep -q glib2-2.61.0-2.fc31 ; then + WITH_VALGRIND=0 + fi + elif grep -q '^PRETTY_NAME="Debian.*sid"$' /etc/os-release; then + if dpkg -s libglib2.0-bin | grep -q '^Version: 2.66.4-2$' ; then + WITH_VALGRIND=0 + fi + fi + if [ "$WITH_VALGRIND" == 0 ]; then + echo "Don't use valgrind due to known issues in other packages." + return 1 + fi + return 0 +} + +############################################################################### + +_print_test_logs() { + echo ">>>> PRINT TEST LOGS $1 (start)" + if test -f test-suite.log; then + cat test-suite.log + fi + echo ">>>> PRINT TEST LOGS $1 (done)" + if _with_valgrind; then + echo ">>>> PRINT VALGRIND LOGS $1 (start)" + find -name '*.valgrind-log' -print0 | xargs -0 grep -H ^ || true + echo ">>>> PRINT VALGRIND LOGS $1 (done)" + fi +} + +run_autotools() { + NOCONFIGURE=1 ./autogen.sh + mkdir ./build + if [ "$_WITH_WERROR" == 1 ]; then + _WITH_WERROR_VAL="error" + else + _WITH_WERROR_VAL="yes" + fi + DISABLE_DEPENDENCY_TRACKING= + if [ $IS_ALPINE = 1 ]; then + DISABLE_DEPENDENCY_TRACKING='--disable-dependency-tracking' + fi + pushd ./build + ../configure \ + --prefix="$PWD/INST" \ + $DISABLE_DEPENDENCY_TRACKING \ + \ + --enable-introspection=$_WITH_DOCS \ + --enable-gtk-doc=$_WITH_DOCS \ + --with-systemd-logind=$_WITH_SYSTEMD_LOGIND \ + --enable-more-warnings="$_WITH_WERROR_VAL" \ + --enable-tests=yes \ + --with-crypto=$_WITH_CRYPTO \ + \ + --with-ebpf=no \ + \ + --with-iwd=yes \ + --with-ofono=yes \ + --enable-teamdctl=$_WITH_LIBTEAM \ + \ + --with-dhcpcanon=yes \ + --with-dhcpcd=yes \ + --with-dhclient=yes \ + \ + --with-netconfig=/bin/nowhere/netconfig \ + --with-resolvconf=/bin/nowhere/resolvconf \ + \ + --enable-ifcfg-rh=yes \ + --enable-ifupdown=yes \ + \ + #end + + if [ "$CONFIGURE_ONLY" != 1 ]; then + make -j 6 + make install + + export NM_TEST_CLIENT_CHECK_L10N=1 + + if ! make check -j 6 -k ; then + _print_test_logs "first-test" + echo ">>>> RUN SECOND TEST (start)" + NMTST_DEBUG="debug,TRACE,no-expect-message" make check -k || : + echo ">>>> RUN SECOND TEST (done)" + _print_test_logs "second-test" + die "autotools test failed" + fi + + if _with_valgrind; then + if ! NMTST_USE_VALGRIND=1 make check -j 3 -k ; then + _print_test_logs "(valgrind test)" + die "autotools+valgrind test failed" + fi + fi + fi + popd +} + +############################################################################### + +run_meson() { + if [ "$_WITH_WERROR" == 1 ]; then + _WITH_WERROR_VAL="--werror" + else + _WITH_WERROR_VAL="" + fi + meson setup build \ + \ + -Dprefix="$PWD/INST" \ + \ + --warnlevel 2 \ + $_WITH_WERROR_VAL \ + \ + -D ld_gc=false \ + -D session_tracking=no \ + -D systemdsystemunitdir=no \ + -D systemd_journal=false \ + -D selinux=false \ + -D libaudit=no \ + -D libpsl=false \ + -D vapi=false \ + -D introspection=$_WITH_DOCS \ + -D qt=false \ + -D crypto=$_WITH_CRYPTO \ + -D docs=$_WITH_DOCS \ + \ + -D ebpf=false \ + \ + -D iwd=true \ + -D ofono=true \ + -D teamdctl=$_WITH_LIBTEAM \ + \ + -D dhclient=/bin/nowhere/dhclient \ + -D dhcpcanon=/bin/nowhere/dhcpcanon \ + -D dhcpcd=/bin/nowhere/dhcpd \ + \ + -D netconfig=/bin/nowhere/netconfig \ + -D resolvconf=/bin/nowhere/resolvconf \ + \ + -D ifcfg_rh=false \ + -D ifupdown=true \ + \ + #end + + export NM_TEST_CLIENT_CHECK_L10N=1 + + if [ "$CONFIGURE_ONLY" != 1 ]; then + ninja -C build -v + ninja -C build install + + if ! meson test -C build -v --print-errorlogs ; then + echo ">>>> RUN SECOND TEST (start)" + NMTST_DEBUG="debug,TRACE,no-expect-message" \ + meson test -C build -v --print-errorlogs || : + echo ">>>> RUN SECOND TEST (done)" + die "meson test failed" + fi + + if _with_valgrind; then + if ! NMTST_USE_VALGRIND=1 meson test -C build -v --print-errorlogs ; then + _print_test_logs "(valgrind test)" + die "meson+valgrind test failed" + fi + fi + fi +} + +############################################################################### + +if [ "$BUILD_TYPE" == autotools ]; then + run_autotools +elif [ "$BUILD_TYPE" == meson ]; then + run_meson +fi + +if [ "$USE_CCACHE" = 1 ]; then + echo "ccache statistics:" + ccache -s +fi diff --git a/contrib/scripts/nm-code-format-container.sh b/contrib/scripts/nm-code-format-container.sh new file mode 100755 index 00000000..7a5ce0d4 --- /dev/null +++ b/contrib/scripts/nm-code-format-container.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -e + +die() { + echo "$@" >&2 + exit 1 +} + +DIR="$(realpath "$(dirname "$0")/../../")" +cd "$DIR" + +# The correct clang-format version is the one from the Fedora version used in our +# gitlab-ci pipeline. Parse it from ".gitlab-ci/config.yml". +FEDORA_VERSION="$(sed '/^ tier: 1/,/^ - name/!d' .gitlab-ci/config.yml | sed -n "s/^ - '\([0-9]\+\)'$/\1/p" | sed -n 1p)" + +test -n "$FEDORA_VERSION" || die "Could not detect the Fedora version in .gitlab-ci/config.yml" + +IMAGENAME="nm-code-format:f$FEDORA_VERSION" + +ARGS=( "$@" ) + +if ! podman image exists "$IMAGENAME" ; then + echo "Building image \"$IMAGENAME\"..." + podman build \ + --squash-all \ + --tag "$IMAGENAME" \ + -f <(cat <<EOF +FROM fedora:$FEDORA_VERSION +RUN dnf upgrade -y +RUN dnf install -y git /usr/bin/clang-format +EOF +) +fi + +CMD=( ./contrib/scripts/nm-code-format.sh "${ARGS[@]}" ) + +podman run \ + --rm \ + --name "nm-code-format-f$FEDORA_VERSION" \ + -v "$DIR:/tmp/NetworkManager:Z" \ + -w /tmp/NetworkManager \ + -e "_NM_CODE_FORMAT_CONTAINER=$IMAGENAME" \ + -ti \ + "$IMAGENAME" \ + "${CMD[@]}" diff --git a/contrib/scripts/nm-code-format.sh b/contrib/scripts/nm-code-format.sh new file mode 100755 index 00000000..3c18cd77 --- /dev/null +++ b/contrib/scripts/nm-code-format.sh @@ -0,0 +1,222 @@ +#!/bin/bash + +set -e + +die() { + printf '%s\n' "$*" >&2 + exit 1 +} + +EXCLUDE_PATHS_TOPLEVEL=( + "src/c-list" + "src/c-rbtree" + "src/c-siphash" + "src/c-stdaux" + "src/libnm-std-aux/unaligned-fundamental.h" + "src/libnm-std-aux/unaligned.h" + "src/libnm-systemd-core/src" + "src/libnm-systemd-shared/src" + "src/linux-headers" + "src/n-acd" + "src/n-dhcp4" +) + +NM_ROOT="$(git rev-parse --show-toplevel)" || die "not inside a git repository" +NM_PREFIX="$(git rev-parse --show-prefix)" || die "not inside a git repository" + +if [ ! -f "$NM_ROOT/.clang-format" ]; then + die "Error: the clang-format file in \"$NM_ROOT\" does not exist" +fi + +if ! command -v clang-format &> /dev/null; then + die "Error: clang-format is not installed. On RHEL/Fedora/CentOS run 'dnf install clang-tools-extra'" +fi + +if test -n "$NM_PREFIX"; then + EXCLUDE_PATHS=() + for e in "${EXCLUDE_PATHS_TOPLEVEL[@]}"; do + REGEX="^$NM_PREFIX([^/].*)$" + if [[ "$e" =~ $REGEX ]]; then + EXCLUDE_PATHS+=("${BASH_REMATCH[1]}") + fi + done +else + EXCLUDE_PATHS=("${EXCLUDE_PATHS_TOPLEVEL[@]}") +fi + +FILES=() +HAS_EXPLICIT_FILES=0 +SHOW_FILENAMES=0 +TEST_ONLY=0 +CHECK_UPSTREAM= + +usage() { + printf "Usage: %s [OPTION]... [FILE]...\n" "$(basename "$0")" + printf "Reformat source files using NetworkManager's code-style.\n\n" + printf "If no file is given the script runs on the whole codebase.\n" + printf "OPTIONS:\n" + printf " -h Print this help message.\n" + printf " -i Reformat files (the default).\n" + printf " -n|--dry-run Only check the files (contrary to \"-i\").\n" + printf " -a|--all Check all files (the default).\n" + printf " -u|--upstream COMMIT Check only files from \`git diff --name-only COMMIT\` (contrary to \"-a\").\n" + printf " This also affects directories given in the [FILE] list, but not files.\n" + printf " If this is the last parameter and COMMIT is unspecified/empty, it defaults to \"main\".\n" + printf " -F|--fast Same as \`-u HEAD^\`.\n" + printf " --show-filenames Only print the filenames that would be checked/formatted\n" + printf " -- Separate options from filenames/directories\n" + if [ -n "${_NM_CODE_FORMAT_CONTAINER+x}" ] ; then + printf "\n" + printf "Command runs inside container image \"$_NM_CODE_FORMAT_CONTAINER\".\n" + printf "Delete/renew image with \`podman rmi \"$_NM_CODE_FORMAT_CONTAINER\"\`.\n" + fi +} + +ls_files_exist() { + local OLD_IFS="$IFS" + local f + + IFS=$'\n' + for f in $(cat) ; do + test -f "$f" && printf '%s\n' "$f" + done + IFS="$OLD_IFS" +} + +ls_files_filter() { + local OLD_IFS="$IFS" + local f + + IFS=$'\n' + for f in $(cat) ; do + local found=1 + local p + for p; do + [[ "$f" = "$p/"* ]] && found= + [[ "$f" = "$p" ]] && found= + done + test -n "$found" && printf '%s\n' "$f" + done + IFS="$OLD_IFS" +} + +g_ls_files() { + local pattern="$1" + shift + + if [ -z "$CHECK_UPSTREAM" ]; then + git ls-files -- "$pattern" + else + git diff --no-renames --name-only "$CHECK_UPSTREAM" -- "$pattern" \ + | ls_files_exist + fi | ls_files_filter "$@" +} + +HAD_DASHDASH=0 +while (( $# )); do + if [ "$HAD_DASHDASH" = 0 ]; then + case "$1" in + -h) + usage + exit 0 + ;; + --show-filenames) + SHOW_FILENAMES=1 + shift + continue + ;; + -a|--all) + CHECK_UPSTREAM= + shift + continue + ;; + -u|--upstream) + shift + CHECK_UPSTREAM="$1" + test -n "$CHECK_UPSTREAM" || CHECK_UPSTREAM=main + shift || : + continue + ;; + -F|--fast) + CHECK_UPSTREAM='HEAD^' + shift + continue + ;; + -n|--dry-run) + TEST_ONLY=1 + shift + continue + ;; + -i) + TEST_ONLY=0 + shift + continue + ;; + --) + HAD_DASHDASH=1 + shift + continue + ;; + esac + fi + if [ -d "$1" ]; then + while IFS='' read -r line; + do FILES+=("$line") + done < <(CHECK_UPSTREAM="$CHECK_UPSTREAM" g_ls_files "${1}/*.[hc]" "${EXCLUDE_PATHS[@]}") + elif [ -f "$1" ]; then + FILES+=("$1") + else + usage >&2 + echo >&2 + die "Unknown argument \"$1\" which also is neither a file nor a directory." + fi + shift + HAS_EXPLICIT_FILES=1 +done + +if [ $HAS_EXPLICIT_FILES = 0 ]; then + while IFS='' read -r line; do + FILES+=("$line") + done < <(CHECK_UPSTREAM="$CHECK_UPSTREAM" g_ls_files '*.[ch]' "${EXCLUDE_PATHS[@]}") +fi + +if [ $SHOW_FILENAMES = 1 ]; then + for f in "${FILES[@]}" ; do + printf '%s\n' "$f" + done + exit 0 +fi + +if [ "${#FILES[@]}" = 0 ]; then + if [ -z "$CHECK_UPSTREAM" ]; then + die "Error: no files to check" + fi + exit 0 +fi + +FLAGS_TEST=( --Werror -n --ferror-limit=1 ) + +if [ $TEST_ONLY = 1 ]; then + # We assume that all formatting is correct. In that mode, passing + # all filenames to clang-format is significantly faster. + # + # Only in case of an error, we iterate over the files one by one + # until we find the first invalid file. + for f in "${FILES[@]}"; do + [ -f "$f" ] || die "Error: file \"$f\" does not exist (or is not a regular file)" + done + clang-format "${FLAGS_TEST[@]}" "${FILES[@]}" &>/dev/null && exit 0 + for f in "${FILES[@]}"; do + [ -f "$f" ] || die "Error: file \"$f\" does not exist (or is not a regular file)" + if ! clang-format "${FLAGS_TEST[@]}" "$f" &>/dev/null; then + FF="$(mktemp)" + trap 'rm -f "$FF"' EXIT + clang-format "$f" 2>/dev/null > "$FF" + git --no-pager diff "$f" "$FF" || : + die "Error: file \"$f\" has style issues."$'\n'"Fix it by running \`\"$0\" -i \"$f\"\` using $(clang-format --version)" + fi + done + die "an unknown error happened." +fi + +clang-format -i "${FILES[@]}" diff --git a/contrib/scripts/nm-copr-build-nm-git-bundle.sh b/contrib/scripts/nm-copr-build-nm-git-bundle.sh new file mode 100755 index 00000000..5447a56e --- /dev/null +++ b/contrib/scripts/nm-copr-build-nm-git-bundle.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# create a nm-git-bundle.git bundle and a SRPM for building it +# as a package. This bundle contains the current git history +# of upstream NetworkManager. +# +# The sole purpose of this is to fetch from the bundle to save +# downloading the entire upstream git repository of NetworkManager. +# +# This script is also used by [1] to generate the SRPM. +# [1] https://copr.fedorainfracloud.org/coprs/networkmanager/NetworkManager-main/package/nm-git-bundle/ +# +# The purpose is the following. We build (many) NetworkManager packages in +# copr. The build process runs a script (contrib/scripts/nm-copr-build.sh) that +# fetches the git repository (and we cannot just do a shallow copy -- because +# the version number is calculated by counts all the commits in the HEAD's +# history). NetworkManager's git repository is relatively large so fetching it +# over and over is wasteful. The idea is to have a recent git-bundle of the +# repository, which is hosted close-by in the copr infrastructure. So the build +# script first tries to download the bundle to get the bulk of the git history, +# before doing additional fetches from the upstream repository. From time to +# time, a new bundle has to be generated in copr. + +set -ex + +if [ -z "$GIT_URL" ]; then + GIT_URL=https://github.com/NetworkManager/NetworkManager + #GIT_URL=https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git +fi + +git clone -n "$GIT_URL" + +pushd NetworkManager + +REFS=( + $(git branch -a | sed -n 's#^ *remotes/origin/\(main\|nm-1-[0-9]\+\)$#\1#p') +) + +unset R +unset H +for R in "${REFS[@]}"; do + H="$(git show-ref --verify --hash "refs/remotes/origin/$R")" + git update-ref "refs/heads/$R" "$H" +done + +git bundle create nm-git-bundle.git "${REFS[@]}" + +popd + +DIR="$(mktemp -d rpmbuild.XXXXXX)" + +mkdir -p "$DIR/SOURCES" +mkdir -p "$DIR/SPECS" + +cat <<EOF > "$DIR/SPECS/nm-git-bundle.spec" +Name: nm-git-bundle +Version: $(date '+%Y%m%d') +Release: $(date '+%H%M%S') +Summary: git-bundle of NetworkManager upstream repository + +License: Public Domain +URL: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/tree/main/contrib/fedora/rpm/nm-git-bundle.spec + +%global GIT_URL 'https://github.com/NetworkManager/NetworkManager' +#global GIT_URL 'https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git' + +Source0: nm-git-bundle.git + + +BuildArch: noarch + + +%description +A git-bundle of NetworkManager upstream git repository. Useful to safe +fetching the entire repository from the internet. + + +%install +mkdir -p %{buildroot}/usr/share/NetworkManager/ +cp %{SOURCE0} %{buildroot}/usr/share/NetworkManager/ + + +%files +/usr/share/NetworkManager/nm-git-bundle.git +EOF + +mv ./NetworkManager/nm-git-bundle.git "$DIR/SOURCES/" + +rpmbuild --define "_topdir $DIR" -bs "$DIR/SPECS/nm-git-bundle.spec" + +mv "$DIR/SRPMS/"nm-git-bundle-*.src.rpm . +mv "$DIR/SPECS/nm-git-bundle.spec" . +mv "$DIR/SOURCES/nm-git-bundle.git" . +rm -rf "$DIR" + diff --git a/contrib/scripts/nm-copr-build.sh b/contrib/scripts/nm-copr-build.sh new file mode 100755 index 00000000..94c804fb --- /dev/null +++ b/contrib/scripts/nm-copr-build.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +# This is the build script used by our copr repository at +# https://copr.fedorainfracloud.org/coprs/networkmanager +# +# On a new upstream release, add new copr jobs named "NetworkManager-X.Y" and +# "NetworkManager-X.Y-debug". +# +# - best, look at the latest copr project and replicate the settings. +# - add a custom build with the following script: +# +# #!/bin/bash +# export GIT_REF=nm-$X-$Y +# export DEBUG=0/1 +# export LTO= +# curl https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/raw/main/contrib/scripts/nm-copr-build.sh | bash +# +# - for certain CentOS/EPEL you need to add https://copr.fedorainfracloud.org/coprs/nmstate/nm-build-deps/ +# as build chroot. See under "Settings/Project Details" for the latest copr project. +# - go to "Settings/Integrations" and find the notification URL for the project. Then +# go to https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/hooks and add +# a push event for the "nm-$X-$Y" branch. +# +# environment variables for this script: +# - GIT_REF: the ref that should be build. Can be "main" or a git sha. +# - DEBUG: set to 1 to build "--with debug". Otherwise the default is a release +# build. +# - LTO: set to 1/0 to build "--with/--without lto", otherwise the default depends +# on the distribution. +# - NM_GIT_BUNDLE: set to a HTTP url where to fetch the nm-git-bundle-*.noarch.rpm +# from. Set to empty to skip it. By default, it fetches the bundle from copr. +# See "contrib/scripts/nm-copr-build-nm-git-bundle.sh" script and +# https://copr.fedorainfracloud.org/coprs/networkmanager/NetworkManager-main/package/nm-git-bundle/ + +set -ex + +if [[ "$DEBUG" == 1 ]]; then + DEBUG="--with debug" +else + DEBUG="--without debug" +fi + +if [ "$LTO" = 0 ]; then + LTO='--without lto' +elif [ "$LTO" = 1 ]; then + LTO='--with lto' +else + LTO= +fi + +if [[ -z "$GIT_REF" ]]; then + echo "\$GIT_REF is not set!" + exit 1 +fi + +mkdir NetworkManager +pushd NetworkManager +git init . + +git remote add origin https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git +git remote add --no-tags github https://github.com/NetworkManager/NetworkManager + +get_nm_git_bundle() { + # try to fetch the refs from nm-git-bundle. + # + # This script runs in copr infrastructure to create the SRPM. + # The idea is that this URL is close and downloading it is cheaper + # than fetching everything from upstream git. + if [ -z "$NM_GIT_BUNDLE" ]; then + if [ -n "${NM_GIT_BUNDLE+x}" ]; then + return 0 + fi + NM_GIT_BUNDLE='https://download.copr.fedorainfracloud.org/results/networkmanager/NetworkManager-main/fedora-38-x86_64/06008259-nm-git-bundle/nm-git-bundle-20230606-102458.noarch.rpm' + fi + mkdir nm-git-bundle + pushd nm-git-bundle + time curl "$NM_GIT_BUNDLE" \ + | rpm2cpio - \ + | cpio -idmv + popd + git remote add nm-git-bundle "$PWD/nm-git-bundle/usr/share/NetworkManager/nm-git-bundle.git" + git fetch nm-git-bundle +} + +get_nm_git_bundle +git fetch github +git fetch origin +git remote remove nm-git-bundle || true + +GIT_SHA="$(git show-ref --verify --hash "$GIT_REF" 2>/dev/null || + git show-ref --verify --hash "refs/remotes/origin/$GIT_REF" 2>/dev/null || + git rev-parse --verify "refs/remotes/origin/$GIT_REF" 2>/dev/null || + git rev-parse --verify "$GIT_REF^{commit}" 2>/dev/null)" + +git checkout -b tmp "$GIT_SHA" + +./contrib/fedora/rpm/build_clean.sh -g -S -w test $DEBUG $LTO -s copr +popd + +mv ./NetworkManager/contrib/fedora/rpm/latest/{SOURCES,SPECS}/* . +rm -rf ./NetworkManager diff --git a/contrib/scripts/nm-import-openconnect b/contrib/scripts/nm-import-openconnect new file mode 100755 index 00000000..f14895d3 --- /dev/null +++ b/contrib/scripts/nm-import-openconnect @@ -0,0 +1,261 @@ +#!/usr/bin/env lua +-- SPDX-License-Identifier: GPL-2.0-or-later +-- +-- Copyright (C) 2015 Red Hat, Inc. +-- + +-- Script for importing/converting OpenConnect VPN configuration files for NetworkManager +-- In general, the implementation follows the logic of import() from +-- https://git.gnome.org/browse/network-manager-openconnect/tree/properties/nm-openconnect.c + +---------------------- +-- Helper functions -- +---------------------- +function read_all(in_file) + local f, msg = io.open(in_file, "r") + if not f then return nil, msg; end + local content = f:read("*all") + f:close() + return content +end + +function uuid() + math.randomseed(os.time()) + local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' + local uuid = string.gsub(template, '[xy]', function (c) + local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb) + return string.format('%x', v) + end) + return uuid +end + +function vpn_settings_to_text(vpn_settings) + local t = {} + for k,v in pairs(vpn_settings) do + t[#t+1] = k.."="..v + end + return table.concat(t, "\n") +end + +function usage() + local basename = string.match(arg[0], '[^/\\]+$') or arg[0] + print(basename .. " - convert/import OpenConnect VPN configuration to NetworkManager") + print("Usage:") + print(" " .. basename .. " <input-file> <output-file>") + print(" - converts OpenConnect VPN config to NetworkManager keyfile") + print("") + print(" " .. basename .. " --import <input-file1> <input-file2> ...") + print(" - imports OpenConnect VPN config(s) to NetworkManager") + os.exit(1) +end + + +------------------------------------------- +-- Functions for VPN options translation -- +------------------------------------------- +function handle_yes(t, option, value) + t[option] = "yes" +end +function handle_generic(t, option, value) + if not value[2] then io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1])) end + t[option] = value[2] +end + +-- global variables +g_con_data = {} +g_vpn_data = {} + +vpn2nm = { + ["Description"] = { nm_opt="id", func=handle_generic, tbl=g_con_data }, + ["Host"] = { nm_opt="gateway", func=handle_generic, tbl=g_vpn_data }, + ["CACert"] = { nm_opt="cacert", func=handle_generic, tbl=g_vpn_data }, + ["Proxy"] = { nm_opt="proxy", func=handle_generic, tbl=g_vpn_data }, + ["CSDEnable"] = { nm_opt="enable_csd_trojan", func=handle_yes, tbl=g_vpn_data }, + ["CSDWrapper"] = { nm_opt="csd_wrapper", func=handle_generic, tbl=g_vpn_data }, + ["UserCertificate"] = { nm_opt="usercert", func=handle_generic, tbl=g_vpn_data }, + ["PrivateKey"] = { nm_opt="userkey", func=handle_generic, tbl=g_vpn_data }, + ["FSID"] = { nm_opt="pem_passphrase_fsid", func=handle_yes, tbl=g_vpn_data }, + ["StokenSource"] = { nm_opt="stoken_source", func=handle_generic, tbl=g_vpn_data }, + ["StokenString"] = { nm_opt="stoken_string", func=handle_generic, tbl=g_vpn_data }, +} + +------------------------------------------------------ +-- Read and convert the config into the global vars -- +------------------------------------------------------ +function read_and_convert(in_file) + local function line_split(str) + -- split at '=' character + local sep, fields = "=", {} + local pattern = string.format("([^%s]+)%s(.+)", sep, sep) + fields[1], fields[2] = str:match(pattern) + return fields + end + + in_text, msg = read_all(in_file) + if not in_text then return false, msg end + + -- loop through the config and convert it + for line in in_text:gmatch("[^\r\n]+") do + repeat + -- skip comments and empty lines + if line:find("^%s*[#;]") or line:find("^%s*$") then break end + -- trim leading and trailing spaces + line = line:find("^%s*$") and "" or line:match("^%s*(.*%S)") + + local words = line_split(line) + local val = vpn2nm[words[1]] + if val then + if type(val) == "table" then val.func(val.tbl, val.nm_opt, words) + else print(string.format("debug: '%s' : val=%s"..val)) end + end + until true + end + + -- check mandatory parameters + if not g_vpn_data["gateway"] then + local msg = in_file .. ": Not a valid OpenConnect VPN configuration" + return false, msg + end + return true +end + +-------------------------------------------------------- +-- Create and write connection file in keyfile format -- +-------------------------------------------------------- +function write_vpn_to_keyfile(in_file, out_file) + connection = [[ +[connection] +id=__NAME_PLACEHOLDER__ +uuid=__UUID_PLACEHOLDER__ +type=vpn +autoconnect=no + +[ipv4] +method=auto +never-default=true + +[ipv6] +method=auto + +[vpn] +service-type=org.freedesktop.NetworkManager.openconnect +]] + + connection = connection .. vpn_settings_to_text(g_vpn_data) + + local con_name = g_con_data["id"] or (out_file:gsub(".*/", "")) + connection = string.gsub(connection, "__NAME_PLACEHOLDER__", con_name) + connection = string.gsub(connection, "__UUID_PLACEHOLDER__", uuid()) + + -- write output file + local f, err = io.open(out_file, "w") + if not f then io.stderr:write(err) return false end + f:write(connection) + f:close() + + local ofname = out_file:gsub(".*/", "") + io.stderr:write("Successfully converted VPN configuration: " .. in_file .. " => " .. out_file .. "\n") + io.stderr:write("To use the connection, do:\n") + io.stderr:write("# cp " .. out_file .. " /etc/NetworkManager/system-connections\n") + io.stderr:write("# chmod 600 /etc/NetworkManager/system-connections/" .. ofname .. "\n") + io.stderr:write("# nmcli con load /etc/NetworkManager/system-connections/" .. ofname .. "\n") + return true +end + +--------------------------------------------- +-- Import VPN connection to NetworkManager -- +--------------------------------------------- +function import_vpn_to_NM(filename) + local lgi = require 'lgi' + local GLib = lgi.GLib + local NM = lgi.NM + + -- function creating NMConnection + local function create_profile(name) + local profile = NM.SimpleConnection.new() + + s_con = NM.SettingConnection.new() + s_vpn = NM.SettingVpn.new() + s_con[NM.SETTING_CONNECTION_ID] = name + s_con[NM.SETTING_CONNECTION_UUID] = uuid() + s_con[NM.SETTING_CONNECTION_TYPE] = "vpn" + s_vpn[NM.SETTING_VPN_SERVICE_TYPE] = "org.freedesktop.NetworkManager.openconnect" + for k,v in pairs(g_vpn_data) do + s_vpn:add_data_item(k, v) + end + + profile:add_setting(s_con) + profile:add_setting(s_vpn) + return profile + end + + -- callback function for add_connection() + local function added_cb(client, result, data) + local con,err,code = client:add_connection_finish(result) + if con then + print(string.format("%s: Imported to NetworkManager: %s - %s", + filename, con:get_uuid(), con:get_id())) + else + io.stderr:write(code .. ": " .. err .. "\n"); + return false + end + main_loop:quit() + end + + local profile_name = g_con_data["id"] or string.match(filename, '[^/\\]+$') or filename + main_loop = GLib.MainLoop(nil, false) + local con = create_profile(profile_name) + local client = NM.Client.new() + + -- send the connection to NetworkManager + client:add_connection_async(con, true, nil, added_cb, nil) + + -- run main loop so that the callback could be called + main_loop:run() + return true +end + + +--------------------------- +-- Main code starts here -- +--------------------------- +local import_mode = false +local infile, outfile + +-- parse command-line arguments +if not arg[1] or arg[1] == "--help" or arg[1] == "-h" then usage() end +if arg[1] == "--import" or arg[1] == "-i" then + infile = arg[2] + if not infile then usage() end + import_mode = true +else + infile = arg[1] + outfile = arg[2] + if not infile or not outfile then usage() end + if arg[3] then usage() end +end + +if import_mode then + -- check if lgi is available + local success,msg = pcall(require, 'lgi') + if not success then + io.stderr:write("Lua lgi module is not available, please install it (usually lua-lgi package)\n") + -- print(msg) + os.exit(1) + end + -- read configs, convert them and import to NM + for i = 2, #arg do + ok, err_msg = read_and_convert(arg[i]) + if ok then import_vpn_to_NM(arg[i]) + else io.stderr:write(err_msg .. "\n") end + -- reset global vars + g_con_data = {} + g_vpn_data = {} + end +else + -- read configs, convert them and write as NM keyfile connection + ok, err_msg = read_and_convert(infile) + if ok then write_vpn_to_keyfile(infile, outfile) + else io.stderr:write(err_msg .. "\n") end +end + diff --git a/contrib/scripts/nm-import-openvpn b/contrib/scripts/nm-import-openvpn new file mode 100755 index 00000000..6c9f39c4 --- /dev/null +++ b/contrib/scripts/nm-import-openvpn @@ -0,0 +1,543 @@ +#!/usr/bin/env lua +-- SPDX-License-Identifier: GPL-2.0-or-later +-- +-- Copyright (C) 2015 Red Hat, Inc. +-- + +-- Script for importing/converting OpenVPN configuration files for NetworkManager +-- In general, the implementation follows the logic of import() from +-- https://git.gnome.org/browse/network-manager-openvpn/tree/properties/import-export.c + + +---------------------- +-- Helper functions -- +---------------------- +function read_all(in_file) + local f, msg = io.open(in_file, "r") + if not f then return nil, msg; end + local content = f:read("*all") + f:close() + return content +end + +function uuid() + math.randomseed(os.time()) + local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' + local uuid = string.gsub(template, '[xy]', function (c) + local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb) + return string.format('%x', v) + end) + return uuid +end + +function unquote(str) + return (string.gsub(str, "^([\"\'])(.*)%1$", "%2")) +end + +function parse_ipv4_to_bytes(ip_addr) + local b1,b2,b3,b4 = ip_addr:match("^(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)$") + b1 = tonumber(b1) + b2 = tonumber(b2) + b3 = tonumber(b3) + b4 = tonumber(b4) + return b1, b2, b3, b4 +end + +function is_ipv4(ip_addr) + local b1,b2,b3,b4 = parse_ipv4_to_bytes(ip_addr) + if not b1 or (b1 > 255) then return false end + if not b2 or (b2 > 255) then return false end + if not b3 or (b3 > 255) then return false end + if not b4 or (b4 > 255) then return false end + return true +end + +function ip_mask_to_prefix(mask) + local b, prefix + local b1,b2,b3,b4 = parse_ipv4_to_bytes(mask) + + if b4 ~= 0 then + prefix = 24 + b = b4 + elseif b3 ~= 0 then + prefix = 16 + b = b3 + elseif b2 ~= 0 then + prefix = 8 + b = b2 + else + prefix = 0 + b = b1 + end + while b ~= 0 do + prefix = prefix + 1 + b = bit32.band(0x000000FF, bit32.lshift(b, 1)) + end + return prefix +end + +function vpn_settings_to_text(vpn_settings) + local t = {} + for k,v in pairs(vpn_settings) do + t[#t+1] = k.."="..v + end + return table.concat(t, "\n") +end + +function usage() + local basename = string.match(arg[0], '[^/\\]+$') or arg[0] + print(basename .. " - convert/import OpenVPN configuration to NetworkManager") + print("Usage:") + print(" " .. basename .. " <input-file> <output-file>") + print(" - converts OpenVPN config to NetworkManager keyfile") + print("") + print(" " .. basename .. " --import <input-file1> <input-file2> ...") + print(" - imports OpenVPN config(s) to NetworkManager") + os.exit(1) +end + + +------------------------------------------- +-- Functions for VPN options translation -- +------------------------------------------- +function set_bool(t, option, value) + g_switches[option] = true +end +function handle_yes(t, option, value) + t[option] = "yes" +end +function handle_generic(t, option, value) + if not value[2] then io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1])) return end + t[option] = value[2] +end +function handle_generic_unquote(t, option, value) + if not value[2] then io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1])) return end + t[option] = unquote(value[2]) +end +function handle_number(t, option, value) + if not value[2] then io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1])) return end + if not tonumber(value[2]) then + io.stderr:write(string.format("Warning: ignoring not numeric value '%s' for option '%s'\n", value[2], value[1])) + return + end + t[option] = value[2] +end +function handle_proto(t, option, value) + if not value[2] then io.stderr:write("Warning: ignoring invalid option 'proto'\n") end + if value[2] == "tcp" or value[3] == "tcp-client" or value[2] == "tcp-server" then + t[option] = "yes" + end +end +function handle_comp_lzo(t, option, value) + value[2] = value[2] or "adaptive" + if value[2] == "no" then + value[2] = "no-by-default" + elseif value[2] ~= "yes" and value[2] ~= "adaptive" then + io.stderr:write(string.format("Warning: ignoring invalid argument '%s' in option 'comp-lzo'\n", value[2])) + return + end + t[option] = value[2] +end +function handle_dev_type(t, option, value) + if value[2] ~= "tun" and value[2] ~= "tap" then + io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1])) + end + t[option] = value[2] +end +function handle_remote(t, option, value) + local rem + if not value[2] then io.stderr:write("Warning: ignoring invalid option 'remote'\n") return end + rem = value[2] + if tonumber(value[3]) then + rem = rem .. ":" .. value[3] + end + if value[4] == "udp" or value[4] == "tcp" then + rem = rem .. ":" .. value[4] + end + if t[option] then + t[option] = t[option] .. " " .. rem + else + t[option] = rem + end + g_switches[value[1]] = true +end +function handle_port(t, option, value) + if tonumber(value[2]) then + t[option] = value[2] + end +end +function handle_proxy(t, option, value) + if not value[2] then io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1])) return end + if value[4] then io.stderr:write(string.format("Warning: the third argument of '%s' is not supported yet\n", value[1])) end + t[option[1]] = string.gsub(value[1], "-proxy", "") + t[option[2]] = value[2] + t[option[3]] = value[3] +end +function handle_ifconfig(t, option, value) + if not (value[2] and value[3]) then io.stderr:write("Warning: ignoring invalid option 'ifconfig'\n") return end + t[option[1]] = value[2] + t[option[2]] = value[3] +end +function handle_keepalive(t, option, value) + if (not (value[2] and value[3])) or (not tonumber(value[2]) or not tonumber(value[3])) then + io.stderr:write("Warning: ignoring invalid option 'keepalive'; two numbers required\n") + return + end + t[option[1]] = value[2] + t[option[2]] = value[3] +end +function handle_path(t, option, value) + if value[1] == "pkcs12" then + t["ca"] = value[2] + t["cert"] = value[2] + t["key"] = value[2] + else + t[option] = value[2] + end +end +function handle_secret(t, option, value) + t[option[1]] = value[2] + t[option[2]] = value[3] + g_switches[value[1]]= true +end +function handle_remote_cert_tls(t, option, value) + if value[2] ~= "client" and value[2] ~= "server" then + io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1])) + return + end + t[option] = value[2] +end +function handle_routes(t, option, value) + if not value[2] then io.stderr:write("Warning: invalid option 'route'\n") return end + netmask = (value[3] and value[3] ~= "default") and value[3] or "255.255.255.255" + gateway = (value[4] and value[4] ~= "default") and value[4] or "0.0.0.0" + metric = (value[5] and value[5] ~= "default") and value[5] or "0" + + if not is_ipv4(value[2]) then + if value[2] == "vpn_gateway" or value[2] == "net_gateway" or value[2] == "remote_host" then + io.stderr:write(string.format("Warning: sorry, the '%s' keyword is not supported by NetworkManager in option '%s'\n", + value[2], value[1])) + else + io.stderr:write(string.format("Warning: '%s' is not a valid IPv4 address in option '%s'\n", value[2], value[1])) + end + return + end + if not is_ipv4(netmask) then + io.stderr:write(string.format("Warning: '%s' is not a valid IPv4 netmask in option '%s'\n", netmask, value[1])) + return + end + if not is_ipv4(gateway) then + if gateway == "vpn_gateway" or gateway == "net_gateway" or gateway == "remote_host" then + io.stderr:write(string.format("Warning: sorry, the '%s' keyword is not supported by NetworkManager in option '%s'\n", + gateway, value[1])) + else + io.stderr:write(string.format("Warning: '%s' is not a valid IPv4 gateway in option '%s'\n", gateway, value[1])) + end + return + end + if not tonumber(metric) then + io.stderr:write(string.format("Warning: '%s' is not a valid metric in option '%s'\n", metric, value[1])) + return + end + + if not t[option] then t[option] = {} end + t[option][#t[option]+1] = {value[2], netmask, gateway, metric} +end +function handle_verify_x509_name(t, option, value) + if not value[2] then io.stderr:write("Warning: missing argument in option 'verify-x509-name'\n") return end + value[2] = unquote(value[2]) + value[3] = value[3] or "subject" + if value[3] ~= "subject" and value[3] ~= "name" and value[3] ~= "name-prefix" then + io.stderr:write(string.format("Warning: ignoring invalid value '%s' for type in option '%s'\n", value[3], value[1])) + return + end + t[option] = value[3] .. ":" .. value[2] +end + +-- global variables +g_vpn_data = {} +g_ip4_data = {} +g_switches = {} + +vpn2nm = { + ["auth"] = { nm_opt="auth", func=handle_generic, tbl=g_vpn_data }, + ["auth-user-pass"] = { nm_opt="auth-user-pass", func=set_bool, tbl={} }, + ["ca"] = { nm_opt="ca", func=handle_path, tbl=g_vpn_data }, + ["cert"] = { nm_opt="cert", func=handle_path, tbl=g_vpn_data }, + ["cipher"] = { nm_opt="cipher", func=handle_generic, tbl=g_vpn_data }, + ["client"] = { nm_opt="client", func=set_bool, tbl={} }, + ["comp-lzo"] = { nm_opt="comp-lzo", func=handle_comp_lzo, tbl=g_vpn_data }, + ["dev"] = { nm_opt="dev", func=handle_generic, tbl=g_vpn_data }, + ["dev-type"] = { nm_opt="dev-type", func=handle_dev_type, tbl=g_vpn_data }, + ["float"] = { nm_opt="float", func=handle_yes, tbl=g_vpn_data }, + ["fragment"] = { nm_opt="fragment-size", func=handle_generic, tbl=g_vpn_data }, + ["http-proxy"] = { nm_opt={"proxy-type", "proxy-server", "proxy-port"}, func=handle_proxy, tbl=g_vpn_data }, + ["http-proxy-retry"] = { nm_opt="proxy-retry", func=handle_yes, tbl=g_vpn_data }, + ["ifconfig"] = { nm_opt={"local-ip", "remote-ip"}, func=handle_ifconfig, tbl=g_vpn_data }, + ["keepalive"] = { nm_opt={"ping", "ping-restart"}, func=handle_keepalive, tbl=g_vpn_data }, + ["key"] = { nm_opt="key", func=handle_path, tbl=g_vpn_data }, + ["keysize"] = { nm_opt="keysize", func=handle_generic, tbl=g_vpn_data }, + ["max-routes"] = { nm_opt="max-routes", func=handle_number, tbl=g_vpn_data }, + ["mssfix"] = { nm_opt="mssfix", func=handle_yes, tbl=g_vpn_data }, + ["ns-cert-type"] = { nm_opt="ns-cert-type", func=handle_remote_cert_tls, tbl=g_vpn_data }, + ["ping"] = { nm_opt="ping", func=handle_number, tbl=g_vpn_data }, + ["ping-exit"] = { nm_opt="ping-exit", func=handle_number, tbl=g_vpn_data }, + ["ping-restart"] = { nm_opt="ping-restart", func=handle_number, tbl=g_vpn_data }, + ["pkcs12"] = { nm_opt="client", func=handle_path, tbl=g_vpn_data }, + ["port"] = { nm_opt="port", func=handle_port, tbl=g_vpn_data }, + ["proto"] = { nm_opt="proto-tcp", func=handle_proto, tbl=g_vpn_data }, + ["remote"] = { nm_opt="remote", func=handle_remote, tbl=g_vpn_data }, + ["remote-cert-tls"] = { nm_opt="remote-cert-tls", func=handle_remote_cert_tls, tbl=g_vpn_data }, + ["remote-random"] = { nm_opt="remote-random", func=handle_yes, tbl=g_vpn_data }, + ["reneg-sec"] = { nm_opt="reneg-seconds", func=handle_generic, tbl=g_vpn_data }, + ["route"] = { nm_opt="routes", func=handle_routes, tbl=g_ip4_data }, + ["rport"] = { nm_opt="port", func=handle_port, tbl=g_vpn_data }, + ["secret"] = { nm_opt={"static-key", "static-key-direction"}, func=handle_secret, tbl=g_vpn_data }, + ["socks-proxy"] = { nm_opt={"proxy-type", "proxy-server", "proxy-port"}, func=handle_proxy, tbl=g_vpn_data }, + ["socks-proxy-retry"] = { nm_opt="proxy-retry", func=handle_yes, tbl=g_vpn_data }, + ["tls-auth"] = { nm_opt={"ta", "ta-dir"}, func=handle_secret, tbl=g_vpn_data }, + ["tls-cipher"] = { nm_opt="tls-cipher", func=handle_generic_unquote, tbl=g_vpn_data }, + ["tls-client"] = { nm_opt="client", func=set_bool, tbl={} }, + ["tls-remote"] = { nm_opt="tls-remote", func=handle_generic_unquote, tbl=g_vpn_data }, + ["tun-ipv6"] = { nm_opt="tun-ipv6", func=handle_yes, tbl=g_vpn_data }, + ["tun-mtu"] = { nm_opt="tunnel-mtu", func=handle_generic, tbl=g_vpn_data }, + ["verify-x509-name"] = { nm_opt="verify-x509-name", func=handle_verify_x509_name,tbl=g_vpn_data }, +} + +------------------------------------------------------------ +-- Read and convert the config into the global g_vpn_data -- +----------------------------------------------------------- +function read_and_convert(in_file) + local function line_split(line) + local t={} + local i, idx = 1, 1 + local delim = "\"" + while true do + local a,b = line:find("%S+", idx) + if not a then break end + + local str = line:sub(a,b) + local quote = nil + if str:sub(1,1) == delim and str:sub(#str,#str) ~= delim then + quote = (line.." "):find(delim.."%s", b + 1) + end + + if quote then + t[i] = line:sub(a, quote) + idx = quote + 1 + else + t[i] = str + idx = b + 1 + end + i = i + 1 + end + return t + end + + in_text, msg = read_all(in_file) + if not in_text then return false, msg end + + -- loop through the config and convert it + for line in in_text:gmatch("[^\r\n]+") do + repeat + -- skip comments and empty lines + if line:find("^%s*[#;]") or line:find("^%s*$") then break end + -- trim leading and trailing spaces + line = line:find("^%s*$") and "" or line:match("^%s*(.*%S)") + + local words = line_split(line) + local val = vpn2nm[words[1]] + if val then + if type(val) == "table" then val.func(val.tbl, val.nm_opt, words) + else print(string.format("debug: '%s' : val=%s"..val)) end + end + until true + end + + -- check some inter-option dependencies + if not g_switches["client"] and not g_switches["secret"] then + local msg = in_file .. ": Not a valid OpenVPN client configuration" + return false, msg + end + if not g_switches["remote"] then + local msg = in_file .. ": Not a valid OpenVPN configuration (no remote)" + return false, msg + end + + -- set 'connection-type' + g_vpn_data["connection-type"] = "tls" + have_sk = g_switches["secret"] ~= nil + have_ca = g_vpn_data["ca"] ~= nil + have_certs = ve_ca and g_vpn_data["cert"] and g_vpn_data["key"] + if g_switches["auth-user-pass"] then + if have_certs then + g_vpn_data["connection-type"] = "password-tls" + elseif have_ca then + g_vpn_data["connection-type"] = "tls" + end + elseif have_certs then g_vpn_data["connection-type"] = "tls" + elseif have_sk then g_vpn_data["connection-type"] = "static-key" + end + return true +end + + +-------------------------------------------------------- +-- Create and write connection file in keyfile format -- +-------------------------------------------------------- +function write_vpn_to_keyfile(in_file, out_file) + connection = [[ +[connection] +id=__NAME_PLACEHOLDER__ +uuid=__UUID_PLACEHOLDER__ +type=vpn +autoconnect=no + +[ipv4] +method=auto +never-default=true +__ROUTES_PLACEHOLDER__ + +[ipv6] +method=auto + +[vpn] +service-type=org.freedesktop.NetworkManager.openvpn +]] + connection = connection .. vpn_settings_to_text(g_vpn_data) + + local routes = "" + for idx, r in ipairs(g_ip4_data["routes"] or {}) do + routes = routes .. string.format("routes%d=%s/%s,%s,%s\n", + idx, r[1], ip_mask_to_prefix(r[2]), r[3], r[4]) + end + + connection = string.gsub(connection, "__NAME_PLACEHOLDER__", (out_file:gsub(".*/", ""))) + connection = string.gsub(connection, "__UUID_PLACEHOLDER__", uuid()) + connection = string.gsub(connection, "__ROUTES_PLACEHOLDER__\n", routes) + + -- write output file + local f, err = io.open(out_file, "w") + if not f then io.stderr:write(err) return false end + f:write(connection) + f:close() + + local ofname = out_file:gsub(".*/", "") + io.stderr:write("Successfully converted VPN configuration: " .. in_file .. " => " .. out_file .. "\n") + io.stderr:write("To use the connection, do:\n") + io.stderr:write("# cp " .. out_file .. " /etc/NetworkManager/system-connections\n") + io.stderr:write("# chmod 600 /etc/NetworkManager/system-connections/" .. ofname .. "\n") + io.stderr:write("# nmcli con load /etc/NetworkManager/system-connections/" .. ofname .. "\n") + return true +end + +--------------------------------------------- +-- Import VPN connection to NetworkManager -- +--------------------------------------------- +function import_vpn_to_NM(filename) + local lgi = require 'lgi' + local GLib = lgi.GLib + local NM = lgi.NM + + -- function creating NMConnection + local function create_profile(name) + local profile = NM.SimpleConnection.new() + + s_con = NM.SettingConnection.new() + s_ip4 = NM.SettingIP4Config.new() + s_vpn = NM.SettingVpn.new() + s_con[NM.SETTING_CONNECTION_ID] = name + s_con[NM.SETTING_CONNECTION_UUID] = uuid() + s_ip4[NM.SETTING_IP_CONFIG_METHOD] = NM.SETTING_IP4_CONFIG_METHOD_AUTO + s_con[NM.SETTING_CONNECTION_TYPE] = "vpn" + s_vpn[NM.SETTING_VPN_SERVICE_TYPE] = "org.freedesktop.NetworkManager.openvpn" + + -- add routes + local AF_INET = 2 + for _, r in ipairs(g_ip4_data["routes"] or {}) do + route = NM.IPRoute.new(AF_INET, r[1], ip_mask_to_prefix(r[2]), r[3], r[4]) + s_ip4:add_route(route) + end + + -- add vpn data + for k,v in pairs(g_vpn_data) do + s_vpn:add_data_item(k, v) + end + + profile:add_setting(s_con) + profile:add_setting(s_vpn) + profile:add_setting(s_ip4) + return profile + end + + -- callback function for add_connection() + local function added_cb(client, result, data) + local con,err,code = client:add_connection_finish(result) + if con then + print(string.format("%s: Imported to NetworkManager: %s - %s", + filename, con:get_uuid(), con:get_id())) + else + io.stderr:write(code .. ": " .. err .. "\n"); + return false + end + main_loop:quit() + end + + local profile_name = string.match(filename, '[^/\\]+$') or filename + main_loop = GLib.MainLoop(nil, false) + local con = create_profile(profile_name) + local client = NM.Client.new() + + -- send the connection to NetworkManager + client:add_connection_async(con, true, nil, added_cb, nil) + + -- run main loop so that the callback could be called + main_loop:run() + return true +end + + +--------------------------- +-- Main code starts here -- +--------------------------- +local import_mode = false +local infile, outfile + +-- parse command-line arguments +if not arg[1] or arg[1] == "--help" or arg[1] == "-h" then usage() end +if arg[1] == "--import" or arg[1] == "-i" then + infile = arg[2] + if not infile then usage() end + import_mode = true +else + infile = arg[1] + outfile = arg[2] + if not infile or not outfile then usage() end + if arg[3] then usage() end +end + +if import_mode then + -- check if lgi is available + local success,msg = pcall(require, 'lgi') + if not success then + io.stderr:write("Lua lgi module is not available, please install it (usually lua-lgi package)\n") + -- print(msg) + os.exit(1) + end + -- read configs, convert them and import to NM + for i = 2, #arg do + ok, err_msg = read_and_convert(arg[i]) + if ok then import_vpn_to_NM(arg[i]) + else io.stderr:write(err_msg .. "\n") end + -- reset global vars + g_vpn_data = {} + g_ip4_data = {} + g_switches = {} + end +else + -- read configs, convert them and write as NM keyfile connection + ok, err_msg = read_and_convert(infile) + if ok then write_vpn_to_keyfile(infile, outfile) + else io.stderr:write(err_msg .. "\n") end +end + diff --git a/contrib/scripts/nm-import-vpnc b/contrib/scripts/nm-import-vpnc new file mode 100755 index 00000000..f7d5debb --- /dev/null +++ b/contrib/scripts/nm-import-vpnc @@ -0,0 +1,416 @@ +#!/usr/bin/env lua +-- SPDX-License-Identifier: GPL-2.0-or-later +-- +-- Copyright (C) 2015 Red Hat, Inc. +-- + +-- Script for importing/converting Cisco VPN configuration files (.pcf) to NetworkManager +-- In general, the implementation follows the logic of import() from +-- https://git.gnome.org/browse/network-manager-vpnc/tree/properties/nm-vpnc.c + +---------------------- +-- Helper functions -- +---------------------- +function read_all(in_file) + local f, msg = io.open(in_file, "r") + if not f then return nil, msg; end + local content = f:read("*all") + f:close() + return content +end + +function uuid() + math.randomseed(os.time()) + local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' + local uuid = string.gsub(template, '[xy]', function (c) + local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb) + return string.format('%x', v) + end) + return uuid +end + +function vpn_settings_to_text(vpn_settings) + local t = {} + for k,v in pairs(vpn_settings) do + t[#t+1] = k.."="..v + end + return table.concat(t, "\n") +end + +function usage() + local basename = string.match(arg[0], '[^/\\]+$') or arg[0] + print(basename .. " - convert/import Cisco VPN (.pcf) configuration to NetworkManager") + print("Usage:") + print(" " .. basename .. " <input-file> <output-file>") + print(" - converts Cisco VPN config to NetworkManager keyfile") + print("") + print(" " .. basename .. " --import <input-file1> <input-file2> ...") + print(" - imports Cisco VPN config(s) to NetworkManager") + os.exit(1) +end + + +------------------------------------------- +-- Functions for VPN options translation -- +------------------------------------------- +function set_option(t, option, value) + g_switches[value[1]] = value[2] +end +function handle_generic(t, option, value) + t[option] = value[2] +end +function handle_yes(t, option, value) + t[option] = "yes" +end +function handle_bool(t, option, value) + if tonumber(value[2]) == 1 then + t[option] = "true" + elseif tonumber(value[2]) == 0 then + t[option] = "false" + else + io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1])) + end +end +function handle_DHGroup(t, option, value) + local dhgroups = { [1]="dh1", [2]="dh2", [5]="dh5" } + dhgroup = dhgroups[tonumber(value[2])] + if not dhgroup then io.stderr:write(string.format("Warning: invalid value for 'DHGroup': %s\n", value[2])) end + t[option] = dhgroup +end +function handle_PeerTimeout(t, option, value) + if not value[2] then io.stderr:write("Warning: ignoring invalid option 'PeerTimeout'\n") end + if tonumber(value[2]) == 0 or (tonumber(value[2]) >=10 and tonumber(value[2] <= 86400)) then + t[option] = value[2] + else io.stderr:write(string.format("Warning: invalid value for 'PeerTimeout': %s\n", value[2])) end +end +function handle_(t, option, value) + io.stderr:write("Warning: enc_GroupPwd: encrypted group passwords are not supported by this script.\n") +end +function handle_TunnelingMode(t, option, value) + if value[2] == 1 then + io.stderr:write("Warning: TCP tunneling is not supported by vpnc. " .. + "The connection will be used with TCP tunneling disabled, " .. + "however it may not work as expected.\n") + end +end +function handle_UseLegacyIKEPort(t, option, value) + if value[2] ~= 0 then + t[option] = 500 + end +end +function handle_routes(t, option, value) + local function splitroutes(str) + local sep, fields = " ", {} + local pattern = string.format("([^%s]+)", sep) + str:gsub(pattern, + function(c) + local c1,c2 = c:match("^(%d+%.%d+%.%d+%.%d+)/(%d+)$") + if c1 then + fields[#fields+1] = { c1, c2 } + else + io.stderr:write("Warning: ignoring invalid route: '" .. c .. "'\n") + end + end) + return fields + end + t[option] = splitroutes(value[2]) +end + +-- global variables - +g_vpn_data = {} +g_vpn_pwds = {} +g_con_data = {} +g_ip4_data = {} +g_switches = {} + +vpn2nm = { + ["Description"] = { nm_opt="id", func=handle_generic, tbl=g_con_data }, + ["InterfaceName"] = { nm_opt="interface-name", func=handle_generic, tbl=g_con_data }, + ["EnableLocalLAN"] = { nm_opt="never-default", func=handle_bool, tbl=g_ip4_data }, + ["X-NM-Routes"] = { nm_opt="routes", func=handle_routes, tbl=g_ip4_data }, + ["Host"] = { nm_opt="IPSec gateway", func=handle_generic, tbl=g_vpn_data }, + ["GroupName"] = { nm_opt="IPSec ID", func=handle_generic, tbl=g_vpn_data }, + ["Username"] = { nm_opt="Xauth username", func=handle_generic, tbl=g_vpn_data }, + ["UserPassword"] = { nm_opt="Xauth password", func=handle_generic, tbl=g_vpn_pwds }, + ["SaveUserPassword"] = { nm_opt="", func=set_option, tbl={} }, + ["GroupPwd"] = { nm_opt="IPSec secret", func=handle_generic, tbl=g_vpn_pwds }, + ["DHGroup"] = { nm_opt="IKE DH Group", func=handle_DHGroup, tbl=g_vpn_data }, + ["NTDomain"] = { nm_opt="Domain", func=handle_generic, tbl=g_vpn_data }, + ["SingleDES"] = { nm_opt="Enable Single DES", func=handle_yes, tbl=g_vpn_data }, + ["EnableNat"] = { nm_opt="", func=set_option, tbl={} }, + ["X-NM-Use-NAT-T"] = { nm_opt="", func=set_option, tbl={} }, + ["X-NM-Force-NAT-T"] = { nm_opt="", func=set_option, tbl={} }, + ["X-NM-SaveGroupPassword"] = { nm_opt="", func=set_option, tbl={} }, + ["UseLegacyIKEPort"] = { nm_opt="Local Port", func=handle_UseLegacyIKEPort, tbl=g_vpn_data }, + ["PeerTimeout"] = { nm_opt="DPD idle timeout (our side)", func=handle_PeerTimeout, tbl=g_vpn_data }, + ["TunnelingMode"] = { nm_opt="", func=handle_TunnelingMode, tbl= {} }, + ["enc_UserPassword"] = { nm_opt="", func=handle_enc_pwd, tbl= {} }, + ["enc_GroupPwd"] = { nm_opt="", func=handle_enc_pwd, tbl= {} }, +} + +------------------------------------------------------ +-- Read and convert the config into the global vars -- +------------------------------------------------------ +function read_and_convert(in_file) + local function line_split(str) + -- split at '=' character + local sep, fields = "=", {} + local pattern = string.format("([^%s]+)%s(.+)", sep, sep) + fields[1], fields[2] = str:match(pattern) + return fields + end + + in_text, msg = read_all(in_file) + if not in_text then return false, msg end + + -- loop through the config and convert it + for line in in_text:gmatch("[^\r\n]+") do + repeat + -- skip comments and empty lines + if line:find("^%s*[#;]") or line:find("^%s*$") then break end + -- trim leading and trailing spaces + line = line:find("^%s*$") and "" or line:match("^%s*(.*%S)") + + local words = line_split(line) + local val = vpn2nm[words[1]] + if val then + if type(val) == "table" then val.func(val.tbl, val.nm_opt, words) + else print(string.format("debug: '%s': val=%s", line, val)) end + end + until true + end + + -- check if mandatory options exist + if not g_vpn_data["IPSec gateway"] then + local msg = in_file .. ": Not a valid Cisco VPN configuration (no Host)" + return false, msg + end + if not g_vpn_data["IPSec ID"] then + local msg = in_file .. ": Not a valid OpenVPN configuration (no GroupName)" + return false, msg + end + + -- process inter-option dependencies + -- NAT traversal mode + local natt_mode = { + NONE = "none", + NATT = "natt", + NATT_ALWAYS = "force-natt", + CISCO = "cisco-udp" + } + g_vpn_data["NAT Traversal Mode"] = natt_mode.CISCO + if tonumber(g_switches["EnableNat"]) == 0 then + g_vpn_data["NAT Traversal Mode"] = natt_mode.NONE + elseif tonumber(g_switches["EnableNat"]) == 1 then + if tonumber(g_switches["X-NM-Force-NAT-T"]) == 1 then + g_vpn_data["NAT Traversal Mode"] = natt_mode.NATT_ALWAYS + elseif tonumber(g_switches["X-NM-Use-NAT-T"]) == 1 then + g_vpn_data["NAT Traversal Mode"] = natt_mode.NATT + end + else + io.stderr:write("Warning: invalid value for EnableNat\n") + g_vpn_data["NAT Traversal Mode"] = natt_mode.CISCO + end + + -- set secret flags + g_vpn_data["Xauth password-flags"] = 1 + if tonumber(g_switches["SaveUserPassword"]) == 1 then + g_vpn_data["xauth-password-type"] = "save" + else + g_vpn_data["Xauth password-flags"] = 3 + end + if g_vpn_data["IPSec ID"] then + g_vpn_data["IPSec ID-flags"] = 1 + end + if g_switches["X-NM-SaveGroupPassword"] then + if tonumber(g_switches["X-NM-SaveGroupPassword"]) == 1 then + g_vpn_data["ipsec-secret-type"] = "save" + g_vpn_data["IPSec ID-flags"] = 1 + else + g_vpn_data["IPSec ID-flags"] = 3 + end + else + g_vpn_data["ipsec-secret-type"] = "save" + end + + return true +end + + +-------------------------------------------------------- +-- Create and write connection file in keyfile format -- +-------------------------------------------------------- +function write_vpn_to_keyfile(in_file, out_file) + connection = [[ +[connection] +id=__NAME_PLACEHOLDER__ +uuid=__UUID_PLACEHOLDER__ +__IFNAME_PLACEHOLDER__ +type=vpn +autoconnect=no + +[ipv4] +method=auto +never-default=__NEVER_DEFAULT_PLACEHOLDER__ +__ROUTES_PLACEHOLDER__ + +[ipv6] +method=auto + +[vpn] +service-type=org.freedesktop.NetworkManager.vpnc +]] + connection = connection .. vpn_settings_to_text(g_vpn_data) + connection = connection .. "\n\n[vpn-secrets]\n" + connection = connection .. vpn_settings_to_text(g_vpn_pwds) + + local con_name = g_con_data["id"] or (out_file:gsub(".*/", "")) + local ifname = g_con_data["interface-name"] + local never_default = g_ip4_data["never-default"] or "false" + local routes = "" + if ifname then ifname = "interface-name="..ifname.."\n" else ifname = "" end + for idx, r in ipairs(g_ip4_data["routes"] or {}) do + routes = routes .. string.format("routes%d=%s/%s\n", idx, r[1], r[2]) + end + + connection = string.gsub(connection, "__NAME_PLACEHOLDER__", con_name) + connection = string.gsub(connection, "__UUID_PLACEHOLDER__", uuid()) + connection = string.gsub(connection, "__IFNAME_PLACEHOLDER__\n", ifname) + connection = string.gsub(connection, "__NEVER_DEFAULT_PLACEHOLDER__", never_default) + connection = string.gsub(connection, "__ROUTES_PLACEHOLDER__\n", routes) + + -- write output file + local f, err = io.open(out_file, "w") + if not f then io.stderr:write(err) return false end + f:write(connection) + f:close() + + local ofname = out_file:gsub(".*/", "") + io.stderr:write("Successfully converted VPN configuration: " .. in_file .. " => " .. out_file .. "\n") + io.stderr:write("To use the connection, do:\n") + io.stderr:write("# cp " .. out_file .. " /etc/NetworkManager/system-connections\n") + io.stderr:write("# chmod 600 /etc/NetworkManager/system-connections/" .. ofname .. "\n") + io.stderr:write("# nmcli con load /etc/NetworkManager/system-connections/" .. ofname .. "\n") + return true +end + +--------------------------------------------- +-- Import VPN connection to NetworkManager -- +--------------------------------------------- +function import_vpn_to_NM(filename) + local lgi = require 'lgi' + local GLib = lgi.GLib + local NM = lgi.NM + + -- function creating NMConnection + local function create_profile(name) + local profile = NM.SimpleConnection.new() + local never_default = g_ip4_data["never-default"] == "true" + + s_con = NM.SettingConnection.new() + s_vpn = NM.SettingVpn.new() + s_ip4 = NM.SettingIP4Config.new() + + s_con[NM.SETTING_CONNECTION_ID] = name + s_con[NM.SETTING_CONNECTION_UUID] = uuid() + s_con[NM.SETTING_CONNECTION_INTERFACE_NAME] = g_con_data["interface-name"] + s_con[NM.SETTING_CONNECTION_TYPE] = "vpn" + s_vpn[NM.SETTING_VPN_SERVICE_TYPE] = "org.freedesktop.NetworkManager.vpnc" + s_ip4[NM.SETTING_IP_CONFIG_METHOD] = NM.SETTING_IP4_CONFIG_METHOD_AUTO + s_ip4[NM.SETTING_IP_CONFIG_NEVER_DEFAULT] = never_default + + -- add routes + local AF_INET = 2 + for _, r in ipairs(g_ip4_data["routes"] or {}) do + route = NM.IPRoute.new(AF_INET, r[1], r[2], nil, -1) + s_ip4:add_route(route) + end + + -- add vpn data + for k,v in pairs(g_vpn_data) do + s_vpn:add_data_item(k, v) + end + -- add vpn secrets + for k,v in pairs(g_vpn_pwds) do + s_vpn:add_secret(k, v) + end + + profile:add_setting(s_con) + profile:add_setting(s_vpn) + profile:add_setting(s_ip4) + return profile + end + + -- callback function for add_connection() + local function added_cb(client, result, data) + local con,err,code = client:add_connection_finish(result) + if con then + print(string.format("%s: Imported to NetworkManager: %s - %s", + filename, con:get_uuid(), con:get_id())) + else + io.stderr:write(code .. ": " .. err .. "\n"); + return false + end + main_loop:quit() + end + + local profile_name = g_con_data["id"] or string.match(filename, '[^/\\]+$') or filename + main_loop = GLib.MainLoop(nil, false) + local con = create_profile(profile_name) + local client = NM.Client.new() + + -- send the connection to NetworkManager + client:add_connection_async(con, true, nil, added_cb, nil) + + -- run main loop so that the callback could be called + main_loop:run() + return true +end + + +--------------------------- +-- Main code starts here -- +--------------------------- +local import_mode = false +local infile, outfile + +-- parse command-line arguments +if not arg[1] or arg[1] == "--help" or arg[1] == "-h" then usage() end +if arg[1] == "--import" or arg[1] == "-i" then + infile = arg[2] + if not infile then usage() end + import_mode = true +else + infile = arg[1] + outfile = arg[2] + if not infile or not outfile then usage() end + if arg[3] then usage() end +end + +if import_mode then + -- check if lgi is available + local success,msg = pcall(require, 'lgi') + if not success then + io.stderr:write("Lua lgi module is not available, please install it (usually lua-lgi package)\n") + -- print(msg) + os.exit(1) + end + -- read configs, convert them and import to NM + for i = 2, #arg do + ok, err_msg = read_and_convert(arg[i]) + if ok then import_vpn_to_NM(arg[i]) + else io.stderr:write(err_msg .. "\n") end + -- reset global vars + g_vpn_data = {} + g_vpn_pwds = {} + g_con_data = {} + g_ip4_data = {} + g_switches = {} + end +else + -- read configs, convert them and write as NM keyfile connection + ok, err_msg = read_and_convert(infile) + if ok then write_vpn_to_keyfile(infile, outfile) + else io.stderr:write(err_msg .. "\n") end +end + diff --git a/contrib/scripts/nm-python-black-format.sh b/contrib/scripts/nm-python-black-format.sh new file mode 100755 index 00000000..eae84fdb --- /dev/null +++ b/contrib/scripts/nm-python-black-format.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +set -e + +_print() { + printf '%s\n' "$*" >&2 +} + +die() { + _print "$*" + exit 1 +} + +NM_ROOT="$(git rev-parse --show-toplevel)" || die "not inside a git repository" +NM_PREFIX="$(git rev-parse --show-prefix)" || die "not inside a git repository" + +cd "$NM_ROOT" || die "failed to cd into \$NM_ROOT\"" + +if [ ! -f "./src/core/main.c" ]; then + die "Error: \"$NM_ROOT\" does not look like NetworkManager source tree" +fi + +BLACK="${BLACK:-black}" + +if ! command -v "$BLACK" &> /dev/null; then + _print "Error: black is not installed. On RHEL/Fedora/CentOS run 'dnf install black'" + exit 77 +fi + +OLD_IFS="$IFS" + +usage() { + printf "Usage: %s [OPTION]...\n" "$(basename "$0")" + printf "Reformat python source files using python black.\n\n" + printf "OPTIONS:\n" + printf " -i Reformat files (this is the default)\n" + printf " -n|--dry-run|--check Only check the files (contrary to \"-i\")\n" + printf " --show-filenames Only print the filenames that would be checked/formatted\n" + printf " -h Print this help message\n" +} + +TEST_ONLY=0 +SHOW_FILENAMES=0 + +while (( $# )); do + case "$1" in + -h) + usage + exit 0 + ;; + -n|--dry-run|--check) + TEST_ONLY=1 + shift + continue + ;; + -i) + TEST_ONLY=0 + shift + continue + ;; + --show-filenames) + SHOW_FILENAMES=1 + shift + continue + ;; + *) + usage + exit 1 + ;; + esac +done + +IFS=$'\n' +FILES=() +FILES+=( $(git ls-tree --name-only -r HEAD | grep '\.py$') ) +FILES+=( $(git grep -l '#!.*\<p[y]thon3\?\>') ) +FILES=( $(printf "%s\n" "${FILES[@]}" | sort -u) ) + +# Filter out paths that are forked from upstream projects and not +# ours to reformat. +FILES=( $( + printf "%s\n" "${FILES[@]}" | + sed \ + -e '/^src\/[cn]-[^/]\+\//d' \ + -e '/^src\/libnm-systemd-[^/]\+\/src\//d' +) ) + +IFS="$OLD_IFS" + +if [ $SHOW_FILENAMES = 1 ]; then + printf '%s\n' "${FILES[@]}" + exit 0 +fi + +EXTRA_ARGS=() +if [ $TEST_ONLY = 1 ]; then + EXTRA_ARGS+=('--check') +fi + +"$BLACK" "${EXTRA_ARGS[@]}" "${FILES[@]}" diff --git a/contrib/scripts/nm-setup-git.sh b/contrib/scripts/nm-setup-git.sh new file mode 100755 index 00000000..32e059ad --- /dev/null +++ b/contrib/scripts/nm-setup-git.sh @@ -0,0 +1,134 @@ +#!/bin/bash + +set -e + +usage() { + printf "%s [--no-test]\n" "$CMD_NAME" + printf "\n" + printf "This script configures (or shows configuration) to the local git, with\n" + printf "settings that might be useful when working on NetworkManager.\n" + printf "\n" + printf "RUn it without arguments, it only prints and shows what it would do.\n" + printf "\n" + printf " --no-test: by default, the script only prints what it\n" + printf " would do. You can also set NO_TEST=1 environment variable.\n" + printf "\n" +} + +get_bool() { + local name="$1" + local val="${!name}" + + case "$val" in + 1|y|yes|Yes|YES|true|True|TRUE|on|On|ON) + echo -n 1 + return 0 + ;; + 0|n|no|No|NO|false|False|FALSE|off|Off|OFF) + echo -n 0 + return 0 + ;; + *) + printf "%s" "$2" + ;; + esac +} + +die() { + echo "ERROR: $*" + exit 1 +} + +_pprint() { + local a + local sp='' + + for a; do + printf "$sp%q" "$a" + sp=' ' + done +} + +call() { + local m="" + + [ "$SKIP" = 1 ] && m="SKIP: " + + if [ "$NO_TEST" != 1 ]; then + printf "WOULD: %s%s\n" "$m" "$(_pprint "$@")" + return 0 + fi + printf "CALL: %s%s\n" "$m" "$(_pprint "$@")" + [ "$SKIP" = 1 ] || "$@" +} + +git_config_reset() { + local key="$1" + local val="$2" + local c=(git config --replace-all "$key" "$val") + + test "$#" -eq 2 || die "invalid arguments to git_config_add(): $@" + + if [ "$(git config --get-all "$key")" = "$val" ]; then + SKIP=1 call "${c[@]}" + return 0 + fi + call "${c[@]}" +} + +git_config_add() { + local key="$1" + local val="$2" + local c=(git config --add "$key" "$val") + + test "$#" -eq 2 || die "invalid arguments to git_config_add(): $@" + + if git config --get-all "$key" | grep -qFx "$val"; then + SKIP=1 call "${c[@]}" + return 0 + fi + call "${c[@]}" +} + +CMD_NAME="$0" +NO_TEST="$(get_bool NO_TEST 0)" + +for a; do + case "$a" in + --no-test) + NO_TEST=1 + ;; + -h|--help) + usage + exit 0 + ;; + *) + usage + die "Invalid argument \"$a\"" + ;; + esac +done + +case "$(git config --get-all remote.origin.url)" in + "https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git"| \ + "git@gitlab.freedesktop.org:NetworkManager/NetworkManager.git"| \ + "ssh://git@gitlab.freedesktop.org/NetworkManager/NetworkManager") + ;; + *) + die "unexpected git repository. Expected that remote.origin.url is set to \"https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git\"" + ;; +esac + +git_config_add blame.ignoreRevsFile '.git-blame-ignore-revs' +git_config_reset blame.markIgnoredLines true +git_config_reset blame.markUnblamableLines true +git_config_add notes.displayref 'refs/notes/bugs' +git_config_add remote.origin.fetch 'refs/notes/bugs:refs/notes/bugs' +git_config_reset remote.origin.pushurl 'git@gitlab.freedesktop.org:NetworkManager/NetworkManager.git' +git_config_add 'alias.backport-merge' '! (git show main:contrib/scripts/git-backport-merge || git show origin/main:contrib/scripts/git-backport-merge) | bash -s -' + +if [ "$NO_TEST" != 1 ]; then + printf "Run with \"--no-test\" or see \"-h\"\n" >&2 + printf "\n" >&2 + printf " \"%s\" --no-test\n" "$CMD_NAME" >&2 +fi diff --git a/contrib/scripts/test-create-many-device-setup.sh b/contrib/scripts/test-create-many-device-setup.sh new file mode 100755 index 00000000..55f2a1c6 --- /dev/null +++ b/contrib/scripts/test-create-many-device-setup.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +set -x + +die() { + printf '%s\n' "$*" >&1 + exit 1 +} + +ARG_OP="$1" +shift +test -n "$ARG_OP" || die "specify the operation (setup, cleanup)" + +test "$USER" = root || die "must run as root" + +NUM_DEVS="${NUM_DEVS:-50}" +NUM_VLAN_DEVS="${NUM_VLAN_DEVS:-0}" + + +DNSMASQ_PIDFILE="/tmp/nm-test-create-many-device-setup.dnsmasq.pid" +NM_TEST_CONF="/etc/NetworkManager/conf.d/99-my-test.conf" +TEST_NETNS="T" + + +_do_service() { + test "$DO_SERVICE" = 1 || return 0 + "$@" +} + +_dnsmasq_kill() { + pkill -F "$DNSMASQ_PIDFILE" + rm -rf "$DNSMASQ_PIDFILE" +} + +_link_delete_all() { + ip link | sed -n 's/^[0-9]\+:.*\(t-[^@:]\+\)@.*/\1/p' | xargs -n 1 ip link delete +} + +cleanup_base() { + ip netns delete "$TEST_NETNS" + _dnsmasq_kill + _link_delete_all + rm -rf "$NM_TEST_CONF" + rm -rf /run/NetworkManager/system-connections/c-*.nmconnection +} + +cmd_cleanup() { + _do_service systemctl stop NetworkManager + cleanup_base + systemctl unmask NetworkManager-dispatcher + systemctl enable NetworkManager-dispatcher + _do_service systemctl start NetworkManager +} + +cmd_setup() { + + _do_service systemctl stop NetworkManager + systemctl mask NetworkManager-dispatcher + systemctl stop NetworkManager-dispatcher + + cleanup_base + + ip netns add "$TEST_NETNS" + ip --netns "$TEST_NETNS" link add t-br0 type bridge + ip --netns "$TEST_NETNS" link set t-br0 type bridge stp_state 0 + ip --netns "$TEST_NETNS" link set t-br0 up + ip --netns "$TEST_NETNS" addr add 172.16.0.1/16 dev t-br0 + ip netns exec "$TEST_NETNS" \ + dnsmasq \ + --conf-file=/dev/null \ + --pid-file="$DNSMASQ_PIDFILE" \ + --no-hosts \ + --keep-in-foreground \ + --bind-interfaces \ + --except-interface=lo \ + --clear-on-reload \ + --listen-address=172.16.0.1 \ + --dhcp-range=172.16.1.1,172.16.20.1,60 \ + --no-ping \ + & + disown + for i in `seq "$NUM_DEVS"`; do + ip --netns "$TEST_NETNS" link add t-a$i type veth peer t-b$i + ip --netns "$TEST_NETNS" link set t-a$i up + ip --netns "$TEST_NETNS" link set t-b$i up master t-br0 + done + for i in `seq "$NUM_VLAN_DEVS"`; do + ip --netns "$TEST_NETNS" link add link t-b1 name t-b1.$i type vlan id $i + ip --netns "$TEST_NETNS" link set t-b1.$i up master t-br0 + done + + cat <<EOF > "$NM_TEST_CONF" +[main] +dhcp=internal +no-auto-default=interface-name:t-a* +[device-99-my-test] +match-device=interface-name:t-a* +managed=1 +[logging] +level=INFO +[connectivity] +enabled=0 +EOF + + _do_service systemctl start NetworkManager + + for i in `seq "$NUM_DEVS"`; do + ip --netns "$TEST_NETNS" link set t-a$i netns $$ + done + + if [ "$DO_ADD_CON" = 1 ]; then + for i in `seq "$NUM_DEVS"`; do + nmcli connection add save no type ethernet con-name c-a$i ifname t-a$i autoconnect no ipv4.method auto ipv6.method auto + done + fi + + if [ "$DO_ADD_VLAN_CON" = 1 ]; then + for i in `seq "$NUM_VLAN_DEVS"`; do + nmcli connection add save no type bridge con-name c-a1.$i-br ifname t-a1.$i.br autoconnect no ipv4.method auto ipv6.method auto bridge.stp 0 + nmcli connection add save no type vlan con-name c-a1.$i-po ifname t-a1.$i.po autoconnect no vlan.id $i vlan.parent t-a1 master c-a1.$i-br slave-type bridge + done + fi +} + + +case "$ARG_OP" in + "setup") + cmd_setup + ;; + "cleanup") + cmd_cleanup + ;; + *) + die "Unknown command \"$ARG_OP\"" + ;; +esac diff --git a/contrib/scripts/test-macsec b/contrib/scripts/test-macsec new file mode 100755 index 00000000..93935865 --- /dev/null +++ b/contrib/scripts/test-macsec @@ -0,0 +1,102 @@ +#!/bin/sh + +# Test for MACsec in PSK mode + +if [ "$#" = 2 ]; then + # DHCP helper + dev=$1 + addr=$2 + net=${addr%.*} + + while [ ! -d "/sys/class/net/$dev" ]; do + sleep 1 + done + + ip a add $addr/24 dev "$dev" + + dnsmasq --conf-file --no-hosts --keep-in-foreground --listen-address=$addr \ + --dhcp-range=$net.250,$net.255,60m -i "$dev" \ + --bind-interface --except-interface=lo + + exit 0 +fi + +TMPDIR=$(mktemp -d /tmp/macsec-XXXXXX) +ADDR=172.16.10.1 +MKA_CAK=00112233445566778899001122334455 +MKA_CKN=5544332211009988776655443322110055443322110099887766554433221100 + +trap 'rm -rf "$TMPDIR"; kill $(jobs -p)' EXIT + +echo "* Setup..." + +# Clean up +ip netns del macsec-ns 2> /dev/null +ip link del macsec-veth 2> /dev/null +# Create namespace +ip netns add macsec-ns +# Create interfaces +ip link add macsec-veth type veth peer name macsec-vethp +# Move interfaces into namespace +ip link set macsec-vethp netns macsec-ns +# Bring up interfaces +ip link set macsec-veth up +ip -n macsec-ns link set macsec-vethp up + +echo "* Start wpa_supplicant..." + +cat <<EOF > $TMPDIR/wpa_supplicant.conf +ctrl_interface=/run/hostapd1 +eapol_version=3 +ap_scan=0 +fast_reauth=1 +network={ + key_mgmt=NONE + eapol_flags=0 + macsec_policy=1 + mka_cak=$MKA_CAK + mka_ckn=$MKA_CKN +} +EOF +ip netns exec macsec-ns wpa_supplicant \ + -c "$TMPDIR/wpa_supplicant.conf" -i macsec-vethp -Dmacsec_linux -dd > /dev/null 2>&1 & +ip netns exec macsec-ns $0 macsec0 $ADDR > /dev/null 2>&1 & + +echo "* Create connections..." + +nmcli connection delete test-macsec+ test-veth+ > /dev/null 2>&1 +nmcli connection add type ethernet ifname macsec-veth con-name test-veth+ \ + ipv4.method disabled ipv6.method ignore +nmcli connection add type macsec con-name test-macsec+ ifname macsec0 \ + connection.autoconnect no \ + macsec.parent macsec-veth macsec.mode psk \ + macsec.mka-cak $MKA_CAK \ + macsec.mka-cak-flags 0 \ + macsec.mka-ckn $MKA_CKN + +echo "* Bring up connections..." +nmcli connection up test-veth+ +nmcli connection up test-macsec+ + +echo "* Test connectivity..." +ping $ADDR -c2 -q > /dev/null +res=$? + +echo "* Clean up..." + +nmcli connection delete test-macsec+ test-veth+ > /dev/null 2>&1 +ip link del macsec-veth 2> /dev/null +ip netns del macsec-ns 2> /dev/null + +echo + +if [ "$res" = 0 ]; then + echo "Success" +else + echo "Failure" +fi + +exit $res + + + diff --git a/contrib/scripts/test-ppp.sh b/contrib/scripts/test-ppp.sh new file mode 100755 index 00000000..c100e976 --- /dev/null +++ b/contrib/scripts/test-ppp.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +# test-ppp.sh: +# +# Test script that creates an netns and connect it with +# veth pairs. On the other end, it runs pppoe-server. +# It also creates a NetworkManager profile that can be activated. +# +# Usage: +# +# ./test-ppp.sh [setup]: create the setup. This implies a "cleanup" +# first. +# ./test-ppp.sh cleanup: cleanup the things that the script created. +set -e + +export IFACE=net1 +export IFACE_PEER=net1-x +export CON_NAME="ppp-$IFACE" +export NETNS=nm-ppp +export PPP_SERVICE=isp +export PPP_AUTH=pap +export PPP_USER=test-user +export PPP_PASSWD=test-passwd +export IP_PEER="192.168.133.6" +export IP_RANGE="192.168.133.100-130" + +die() { + printf '%s\n' "$*" >&2 + exit 1 +} + +do_cleanup() { + pkill -F "/tmp/nm-test-ppp-$IFACE.pid" pppoe-server &>/dev/null || : + rm -rf \ + "/tmp/nm-test-ppp-$IFACE.pid" \ + "/tmp/nm-test-ppp-allip-$IFACE" \ + "/tmp/nm-test-ppp-pppoe-server-options-$IFACE" \ + "/tmp/nm-test-ppp-$IFACE-$PPP_AUTH-secrets" + ip --netns "$NETNS" link delete "$IFACE_PEER" &>/dev/null || : + ip netns delete "$NETNS" &>/dev/null || : + + nmcli connection delete id ppp-net1 || : +} + +do_setup() { + do_cleanup + + ip netns add "$NETNS" + ip --netns "$NETNS" link add "$IFACE" type veth peer "$IFACE_PEER" + ip --netns "$NETNS" link set "$IFACE_PEER" up + + ip --netns "$NETNS" addr add "$IP_PEER/24" dev "$IFACE_PEER" + + echo "$IP_RANGE" > "/tmp/nm-test-ppp-allip-$IFACE" + + cat <<EOF > "/tmp/nm-test-ppp-pppoe-server-options-$IFACE" +require-$PPP_AUTH +lcp-echo-interval 10 +lcp-echo-failure 2 +ms-dns 8.8.8.8 +ms-dns 8.8.4.4 +netmask 255.255.255.0 +defaultroute +noipdefault +usepeerdns +EOF + + echo "$PPP_USER * $PPP_PASSWD $IP_PEER" > "/tmp/nm-test-ppp-$IFACE-$PPP_AUTH-secrets" + chmod 600 "/tmp/nm-test-ppp-$IFACE-$PPP_AUTH-secrets" + mkdir -p /etc/ppp + touch "/etc/ppp/$PPP_AUTH-secrets" + ip netns exec "$NETNS" bash -ex <( + cat <<'EOF' + mount -o bind "/tmp/nm-test-ppp-$IFACE-$PPP_AUTH-secrets" "/etc/ppp/$PPP_AUTH-secrets" && + exec pppoe-server \ + -X "/tmp/nm-test-ppp-$IFACE.pid" \ + -S "$PPP_SERVICE" \ + -C "$PPP_SERVICE" \ + -L "$IP_PEER" \ + -p "/tmp/nm-test-ppp-allip-$IFACE" \ + -I "$IFACE_PEER" \ + -O "/tmp/nm-test-ppp-pppoe-server-options-$IFACE" +EOF +) & + + ip --netns "$NETNS" link set "$IFACE" netns $$ + + nmcli connection add \ + type pppoe \ + con-name "$CON_NAME" \ + ifname "ppp-$IFACE" \ + pppoe.parent "$IFACE" \ + service "$PPP_SERVICE" \ + username "$PPP_USER" \ + password "$PPP_PASSWD" \ + autoconnect no +} + +CMD="${1-setup}" +case "$CMD" in + setup| \ + cleanup) + "do_$CMD" + ;; + *) + die "invalid command $1" + ;; +esac diff --git a/contrib/scripts/test-prefix-delegation.sh b/contrib/scripts/test-prefix-delegation.sh new file mode 100755 index 00000000..7fc4140e --- /dev/null +++ b/contrib/scripts/test-prefix-delegation.sh @@ -0,0 +1,151 @@ +#!/bin/sh + +# Usage: ./test-prefix-delegation {ll|slaac|dhcp-stateful|dhcp-stateless} + +MODE=${1:-dhcp-stateful} + +cleanup() +{ + pkill -F dhcpd.pid + pkill -F radvd.pid + rm -f radvd.conf + rm -f dhcpd.conf + rm -f leases.conf + nmcli connection delete v1+ v2+ + ip netns del ns1 + ip netns del ns2 + ip link del v1 + ip link del v2 +} + +require() +{ + if ! command -v "$1" > /dev/null ; then + echo " *** Error: command '$1' not found" + exit 1 + fi +} + +exit_hook() +{ + cleanup > /dev/null 2>&1 +} + +require nmcli +require ip +require jq +require radvd +require dhcpd + +unalias ip 2> /dev/null + +cleanup +trap exit_hook EXIT + +# ns1 is the 'upstream' namespace that provides IPv6 connectivity +# through RA and DHCPv6. The DHCP server also acts as a delegating +# router for /60 prefixes. + +# ns2 is the 'downstream' namespace where a client obtains IPv6 +# connectivity through RA from NM. + +# NM is in the default namespace and has a connection to ns1 with +# ipv6.method=auto and to ns2 with ipv6.method=shared. + +ip netns add ns1 +ip netns add ns2 + +ip link add v1 type veth peer name v1p +ip link add v2 type veth peer name v2p + +ip link set v1p netns ns1 +ip link set v2p netns ns2 + +ip link set v1 up +ip link set v2 up + +ip -n ns1 link set v1p up +ip -n ns1 addr add dev v1p fc01::1/64 + +ip -n ns2 link set v2p up + +if [ "$MODE" = ll ]; then + adv_managed=off + adv_other=off +elif [ "$MODE" = slaac ]; then + adv_managed=off + adv_other=off + adv_prefix="prefix fc01::/64 {AdvOnLink on; AdvAutonomous on; AdvRouterAddr off; };" +elif [ "$MODE" = dhcp-stateless ]; then + adv_managed=off + adv_other=on + adv_prefix="prefix fc01::/64 {AdvOnLink on; AdvAutonomous on; AdvRouterAddr off; };" +elif [ "$MODE" = dhcp-stateful ]; then + adv_managed=on + adv_other=off + dhcp_range="range6 fc01::1000 fc01::ffff;" +else + echo "Unknown mode '$MODE'" + exit 1 +fi + +echo "Starting in $MODE mode..." + +cat > radvd.conf <<EOF +interface v1p { + AdvSendAdvert on; + AdvManagedFlag ${adv_managed}; + AdvOtherConfigFlag ${adv_other}; + MinRtrAdvInterval 3; + MaxRtrAdvInterval 60; + ${adv_prefix} +}; +EOF + +cat > dhcpd.conf <<EOF +subnet6 fc01::/64 { + ${dhcp_range} + prefix6 fc01:bbbb:1:: fc01:bbbb:2:: / 60; + option dhcp6.name-servers fc01::8888; +} +EOF + +echo > leases.conf +ip netns exec ns1 radvd -n -C radvd.conf -p radvd.pid & +ip netns exec ns1 dhcpd -6 -d -cf dhcpd.conf -lf leases.conf -pf dhcpd.pid & + +nmcli connection add type ethernet ifname v1 con-name v1+ ipv4.method disabled ipv6.method auto autoconnect no +nmcli connection add type ethernet ifname v2 con-name v2+ ipv4.method disabled ipv6.method shared autoconnect no + +nmcli connection up v1+ + +sleep 5 + +nmcli connection up v2+ + +sleep 5 + +ip a show dev v1 +ip a show dev v2 + +addr=$(ip -j addr show dev v1 | jq -r '.[0].addr_info[] | select(.scope=="link")'.local) +prefix="fc01:bbbb:1::/32" +ip netns exec ns1 ip route add $prefix via $addr dev v1p + +# kernel does IPv6 autoconf in ns2 ... + +sleep 10 + +# check connectivity to ns1 +if ! ip -n ns2 a show dev v2p | grep 'fc01:bbbb:[a-f0-9\:]\+/64'; then + ip -n ns2 a show dev v2p + echo "ERROR: no address" + exit 1 +fi + +if ! ip netns exec ns2 ping -c2 fc01::1; then + echo "ERROR: ping failed" + exit 1 +fi + +echo "OK" |