From ad2868da363f750487ccd8bc688ce579f67b4a88 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Fri, 14 Jul 2023 16:18:24 +0200 Subject: [PATCH 1/4] [type/__nsswitch] New type --- type/__nsswitch/explorer/sources | 51 +++++++++ type/__nsswitch/gencode-remote | 114 ++++++++++++++++++++ type/__nsswitch/man.rst | 73 +++++++++++++ type/__nsswitch/nonparallel | 0 type/__nsswitch/parameter/optional_multiple | 1 + 5 files changed, 239 insertions(+) create mode 100755 type/__nsswitch/explorer/sources create mode 100755 type/__nsswitch/gencode-remote create mode 100644 type/__nsswitch/man.rst create mode 100644 type/__nsswitch/nonparallel create mode 100644 type/__nsswitch/parameter/optional_multiple diff --git a/type/__nsswitch/explorer/sources b/type/__nsswitch/explorer/sources new file mode 100755 index 000000000..7b990b836 --- /dev/null +++ b/type/__nsswitch/explorer/sources @@ -0,0 +1,51 @@ +#!/bin/sh -e +# +# 2023,2025 Dennis Camera (dennis.camera at riiengineering.ch) +# +# This file is part of skonfig-base. +# +# skonfig-base 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 3 of the License, or +# (at your option) any later version. +# +# skonfig-base is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with skonfig-base. If not, see . +# +# Prints the sources currently configured for the database. +# + +test -f /etc/nsswitch.conf || exit 0 + +awk -v db_name="${__object_id:?}" ' +# prepare line +{ + # remove comments + sub(/[ \\t]*#.*$/, "") + # merge line continuations to single line + while (/\\$/) { + sub(/\\$/, (0 < (getline _nl) ? _nl : "")) + } +} + +$1 == db_name ":" { + # store found line, because in nsswitch.conf(5) the last match wins + line = $0 +} + +END { + if (line) { + $0 = line + + for (i = 2; i <= NF; ++i) { + printf "%s%s", (i>2 ? " " : ""), $i + } + printf ORS + } +} +' /etc/nsswitch.conf diff --git a/type/__nsswitch/gencode-remote b/type/__nsswitch/gencode-remote new file mode 100755 index 000000000..4c5b9497a --- /dev/null +++ b/type/__nsswitch/gencode-remote @@ -0,0 +1,114 @@ +#!/bin/sh -e +# +# 2023 Dennis Camera (dennis.camera at riiengineering.ch) +# +# This file is part of skonfig-base. +# +# skonfig-base 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 3 of the License, or +# (at your option) any later version. +# +# skonfig-base is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with skonfig-base. If not, see . +# + +join_lines() { + test -r "$2" || return 0 + awk -v sep="$1" 'NR>1{printf "%s",sep}{printf "%s",$0}END{printf ORS}' "$2" +} + +if + join_lines ' ' "${__object:?}/parameter/source" \ + | cmp -s "${__object:?}/explorer/sources" - +then + # no changes needed + exit 0 +fi + + +if ! test -s "${__object:?}/explorer/sources" +then + # possibly the file does not exist + cat <<'EOF' +test -f /etc/nsswitch.conf || ( + umask 0022 + :>/etc/nsswitch.conf + chown 0:0 /etc/nsswitch.conf +) + +EOF +fi + +# update nsswitch.conf(5) + +# NOTE: the updated content is cat(1) into /etc/nsswitch.conf to achieve the +# correct behaviour if /etc/nsswitch.conf is a symbolic link. +cat <<'EOF' +awk -v db_name="${__object_id:?}" -v source_file="${__object:?}/parameter/source" ' +function get_indent( i) { + # FIXME: handle tab stops? + i = index($0, $1) - 1 + i += length($1) + i += index(substr($0, i+1), $2) - 1 + return i +} +function guess_indent(key, cnt, il, i) { + # set initial cnt = 2 to ignore indentations which only occur once + il = (length(key) + 2); cnt = 2 + + for (i in indent) { + if (indent[i] > cnt) { il = i } + } + return il +} + +BEGIN { + while (0 < (getline src < source_file)) { + sources = sources (sources ? " " : "") src + } + close(source_file) +} + +$1 ~ /^[^#].*:/ { + # looks like an entry + db = substr($1, 1, index($1, ":") - 1) + + i = get_indent() + if (i > (index($0, $1) + length($1))) { + # only remember if there is more than one space between db and sources + indent[i]++ + } +} + +db == db_name { + if (!f) { + # only replace the first occurrence + if (sources) { + match($0, /:[ \t]*/) + printf "%s%s" ORS, substr($0, 1, RSTART + RLENGTH - 1), sources + } + f = 1 + } + + while (/\\$/) { getline } + next +} + +{ print } + +END { + if (!f && sources) { + # append new entry + printf ("%-" guess_indent(db_name) "s%s" ORS), db_name ":", sources + } +} +' /etc/nsswitch.conf >/etc/nsswitch.conf.tmp \ +&& cat /etc/nsswitch.conf.tmp >/etc/nsswitch.conf +rm -f /etc/nsswitch.conf.tmp +EOF diff --git a/type/__nsswitch/man.rst b/type/__nsswitch/man.rst new file mode 100644 index 000000000..3aa0b6198 --- /dev/null +++ b/type/__nsswitch/man.rst @@ -0,0 +1,73 @@ +cdist-type__nsswitch(7) +======================= + +NAME +---- +cdist-type__nsswitch - manage Name Service Switch database configuration + + +DESCRIPTION +----------- +This type can be used to manage database configurations in the +:strong:`nsswitch.conf`\ (5) file. + +This type does no checking whether your operating system/libc respects the +nsswitch.conf file, this job is up to you. + + +OPTIONAL PARAMETERS +------------------- +source + Specify the name of a source for this database (with optional criteria + appended to the source name in square brackets ``[]``). + See :strong:`nsswitch.conf`\ (5) for the options supported by your operating + system. + + Can be used multiple times so specify multiple sources which will be queried + in the order specified. + + If no sources are given, the database entry will be removed from + :strong:`nsswitch.conf`\ (5). + + +EXAMPLES +-------- + +.. code-block:: sh + + # Configure NSS to query LDAP for users/groups (Linux) + __nsswitch passwd \ + --source ldap \ + --source files + __nsswitch group \ + --source ldap \ + --source files + __nsswitch shadow \ + --source ldap \ + --source files + + # Configure NSS host lookup to include mDNS (using libnss-mdns on Linux) + # Other OSs may use different source names, e.g. mdns_minimal is called + # multicast_dns on NetBSD + __nsswitch hosts \ + --source files \ + --source 'mdns_minimal [NOTFOUND=return]' \ + --source dns + + +SEE ALSO +-------- +* :strong:`nsswitch.conf`\ (5) + + +AUTHORS +------- +* Dennis Camera + + +COPYING +------- +Copyright \(C) 2023, 2025 Dennis Camera. +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 3 of +the License, or (at your option) any later version. diff --git a/type/__nsswitch/nonparallel b/type/__nsswitch/nonparallel new file mode 100644 index 000000000..e69de29bb diff --git a/type/__nsswitch/parameter/optional_multiple b/type/__nsswitch/parameter/optional_multiple new file mode 100644 index 000000000..5a18cd2fb --- /dev/null +++ b/type/__nsswitch/parameter/optional_multiple @@ -0,0 +1 @@ +source From 3fc40139a166030edcb4a0dfc66373af68b6da63 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 5 Oct 2025 17:51:49 +0200 Subject: [PATCH 2/4] [type/__nsswitch] Block Red Hat derivatives until we implement authconfig/authselect --- type/__nsswitch/gencode-remote | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/type/__nsswitch/gencode-remote b/type/__nsswitch/gencode-remote index 4c5b9497a..961786d7f 100755 --- a/type/__nsswitch/gencode-remote +++ b/type/__nsswitch/gencode-remote @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2023 Dennis Camera (dennis.camera at riiengineering.ch) +# 2023,2025 Dennis Camera (dennis.camera at riiengineering.ch) # # This file is part of skonfig-base. # @@ -31,6 +31,15 @@ then exit 0 fi +os=$(cat "${__global:?}/explorer/os") +case ${os} +in + (fedora|redhat|centos|almalinux|rocky|oraclelinux|scientific|amazon) + echo 'This type currently does not work on Red Hat, because it does not support authconfig/authselect.' >&2 + echo 'Please contribute an implementation for it if you can.' >&2 + exit 1 + ;; +esac if ! test -s "${__object:?}/explorer/sources" then From ed550d784c61d60a6893679afe2f962066231ad1 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 5 Oct 2025 18:48:27 +0200 Subject: [PATCH 3/4] [type/__nsswitch] Block Solaris 11 until we implement SMF support --- type/__nsswitch/gencode-remote | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/type/__nsswitch/gencode-remote b/type/__nsswitch/gencode-remote index 961786d7f..6c3541e91 100755 --- a/type/__nsswitch/gencode-remote +++ b/type/__nsswitch/gencode-remote @@ -39,6 +39,18 @@ in echo 'Please contribute an implementation for it if you can.' >&2 exit 1 ;; + (solaris) + case $(cat "${__global:?}/explorer/os_version") + in + (5.11|5.11.*|'') + # Solaris 11 manages name service switch information in SMF + # https://docs.oracle.com/cd/E23824_01/html/821-1455/c8switch-18904.html + echo 'This type currently does not work on Solaris 11, because it does not support managing the information in SMF.' >&2 + echo 'Please contribute an implementation for it if you can.' >&2 + exit 1 + ;; + esac + ;; esac if ! test -s "${__object:?}/explorer/sources" From 3d1982dd97df73af7cd21af015bac6d6132002da Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 5 Oct 2025 20:27:25 +0200 Subject: [PATCH 4/4] [type/__nsswitch] Improve updater scripts (handle tabs) Detect tab indented nsswitch.conf files and indent new entries with tabs as well. Require a clear tabulation level (> 50% of all tabulated lines) to tabulate new entries. When fewer lines are tabulated, either we have mis-detected it or the file is chaos, so we ignore what we detect. --- type/__nsswitch/explorer/sources | 3 +- type/__nsswitch/gencode-remote | 72 +++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/type/__nsswitch/explorer/sources b/type/__nsswitch/explorer/sources index 7b990b836..7aa71d60e 100755 --- a/type/__nsswitch/explorer/sources +++ b/type/__nsswitch/explorer/sources @@ -27,7 +27,8 @@ awk -v db_name="${__object_id:?}" ' { # remove comments sub(/[ \\t]*#.*$/, "") - # merge line continuations to single line + # merge line continuations to single line (line continuations do not apply + # to comments, btw). while (/\\$/) { sub(/\\$/, (0 < (getline _nl) ? _nl : "")) } diff --git a/type/__nsswitch/gencode-remote b/type/__nsswitch/gencode-remote index 6c3541e91..357469ade 100755 --- a/type/__nsswitch/gencode-remote +++ b/type/__nsswitch/gencode-remote @@ -72,52 +72,74 @@ fi # correct behaviour if /etc/nsswitch.conf is a symbolic link. cat <<'EOF' awk -v db_name="${__object_id:?}" -v source_file="${__object:?}/parameter/source" ' -function get_indent( i) { - # FIXME: handle tab stops? - i = index($0, $1) - 1 +function update_indents( i, _l) { + _l = $0 + while (_l ~ /[\t]/) { + # expand tab stops + i = index(_l, "\t")-1 + _l = substr(_l, 1, i) sprintf("%"(TAB_WIDTH - (i % TAB_WIDTH))"s", "") substr(_l, i+2) + } + + i = index(_l, $1) - 1 i += length($1) - i += index(substr($0, i+1), $2) - 1 - return i + i += index(substr(_l, i+1), $2) - 1 + + if (/[\t]/ || i > (index(_l, $1) + length($1))) { + # only remember indentation if there are at least two spaces or a tab + indents[i]++ + num_indents++ + num_indents_tabs += /[\t]/ + } } -function guess_indent(key, cnt, il, i) { +function fmt_db(db, cnt, cnt_total, il, i) { # set initial cnt = 2 to ignore indentations which only occur once - il = (length(key) + 2); cnt = 2 + il = (length(db) + 1); cnt = 2 - for (i in indent) { - if (indent[i] > cnt) { il = i } + for (i in indents) { + cnt_total += indents[i] + if (indents[i] > cnt) { il = i; cnt = indents[i] } + } + + if ((cnt * 2) <= cnt_total) { + # no clear indentation style (< 50%), ignore + return db " " + } + + if ((2 * num_indents_tabs) > num_indents) { + # this file uses tabs predominantly + cnt = (il - length(db)) / TAB_WIDTH + for (i = 0; i < cnt; ++i) { db = db "\t" } + return db + } else { + return sprintf("%-"il"s", db) } - return il } BEGIN { + TAB_WIDTH = 8 + while (0 < (getline src < source_file)) { sources = sources (sources ? " " : "") src } close(source_file) } -$1 ~ /^[^#].*:/ { +$1 ~ /:$/ { # looks like an entry - db = substr($1, 1, index($1, ":") - 1) - - i = get_indent() - if (i > (index($0, $1) + length($1))) { - # only remember if there is more than one space between db and sources - indent[i]++ - } -} + update_indents() -db == db_name { - if (!f) { + if ($1 == db_name":") { # only replace the first occurrence - if (sources) { + if (!f && sources) { match($0, /:[ \t]*/) printf "%s%s" ORS, substr($0, 1, RSTART + RLENGTH - 1), sources } f = 1 + while (/\\$/) { getline } + } else { + print + while (/\\$/) { getline; print } } - - while (/\\$/) { getline } next } @@ -126,7 +148,7 @@ db == db_name { END { if (!f && sources) { # append new entry - printf ("%-" guess_indent(db_name) "s%s" ORS), db_name ":", sources + printf "%s%s" ORS, fmt_db(db_name":"), sources } } ' /etc/nsswitch.conf >/etc/nsswitch.conf.tmp \