master
text 337 lines 8.86 KB
Raw
1 #!/usr/bin/env sh
2
3 if [ "${container+x}" = "x" ]; then
4 _container_saved=$container
5 fi
6
7 # shellcheck disable=SC1091
8 [ -f /etc/profile ] && . /etc/profile
9
10 if [ "${_container_saved+x}" = "x" ]; then
11 container=$_container_saved
12 else
13 unset container
14 fi
15
16 set -e
17
18 script_dir="$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd -P)"
19
20 usage() {
21 check_directories
22 cat <<EOF
23 USAGE:
24 ${0} [options] FILENAME
25
26 Copy and edit the stock config file named: FILENAME
27 if FILENAME is already copied, it will be edited as-is.
28
29 Stock config files at: '${NETDATA_STOCK_CONFIG_DIR}'
30 User config files at: '${NETDATA_USER_CONFIG_DIR}'
31
32 The editor to use can be specified either by setting the EDITOR
33 environment variable, or by using the --editor option.
34
35 The file to edit can also be specified using the --file option.
36
37 For a list of known config files, run '${0} --list'
38 EOF
39 exit 0
40 }
41
42 error() {
43 echo >&2 "ERROR: ${1}"
44 }
45
46 abspath() {
47 if [ -d "${1}/" ]; then
48 echo "$(cd "${1}" && /usr/bin/env PWD= pwd -P)/"
49 elif [ -f "${1}" ]; then
50 echo "$(cd "$(dirname "${1}")" && /usr/bin/env PWD= pwd -P)/$(basename "${1}")"
51 elif echo "${1}" | grep -q '/'; then
52 if echo "${1}" | grep -q '^/'; then
53 mkdir -p "$(dirname "${1}")"
54 echo "$(cd "$(dirname "${1}")" && /usr/bin/env PWD= pwd -P)/$(basename "${1}")"
55 else
56 mkdir -p "${script_dir}/$(dirname "${1}")"
57 echo "${script_dir}/${1}"
58 fi
59 else
60 echo "${script_dir}/${1}"
61 fi
62 }
63
64 is_prefix() {
65 echo "${2}" | grep -qE "^${1}"
66 return $?
67 }
68
69 check_directories() {
70 if [ -f "${script_dir}/.container-hostname" ]; then
71 NETDATA_USER_CONFIG_DIR="${script_dir}"
72 NETDATA_STOCK_CONFIG_DIR="/usr/lib/netdata/conf.d"
73 return
74 fi
75
76 if [ -e "${script_dir}/.environment" ]; then
77 OLDPATH="${PATH}"
78 # shellcheck disable=SC1091
79 . "${script_dir}/.environment"
80 PATH="${OLDPATH}"
81 fi
82
83 if [ -n "${NETDATA_PREFIX}" ] && [ -d "${NETDATA_PREFIX}/usr/lib/netdata/conf.d" ]; then
84 stock_dir="${NETDATA_PREFIX}/usr/lib/netdata/conf.d"
85 elif [ -n "${NETDATA_PREFIX}" ] && [ -d "${NETDATA_PREFIX}/lib/netdata/conf.d" ]; then
86 stock_dir="${NETDATA_PREFIX}/lib/netdata/conf.d"
87 elif [ -d "${script_dir}/../../usr/lib/netdata/conf.d" ]; then
88 stock_dir="${script_dir}/../../usr/lib/netdata/conf.d"
89 elif [ -d "${script_dir}/../../lib/netdata/conf.d" ]; then
90 stock_dir="${script_dir}/../../lib/netdata/conf.d"
91 elif [ -d "/usr/lib/netdata/conf.d" ]; then
92 stock_dir="/usr/lib/netdata/conf.d"
93 fi
94
95 [ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="${script_dir}"
96 [ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="${stock_dir}"
97
98 if [ -z "${NETDATA_STOCK_CONFIG_DIR}" ]; then
99 error "Unable to find stock config directory."
100 exit 1
101 fi
102 }
103
104 check_editor() {
105 if [ -z "${editor}" ]; then
106 if [ -n "${EDITOR}" ] && command -v "${EDITOR}" >/dev/null 2>&1; then
107 editor="${EDITOR}"
108 elif command -v editor >/dev/null 2>&1; then
109 editor="editor"
110 elif command -v vi >/dev/null 2>&1; then
111 editor="vi"
112 else
113 error "Unable to find a usable editor, tried \${EDITOR} (${EDITOR}), editor, and vi."
114 exit 1
115 fi
116 elif ! command -v "${editor}" >/dev/null 2>&1; then
117 error "Unable to locate user specified editor ${editor}, is it in your PATH?"
118 exit 1
119 fi
120 }
121
122 running_in_container() {
123 [ -e /.dockerenv ] && return 0
124 [ -e /.dockerinit ] && return 0
125 [ -e /run/.containerenv ] && return 0
126 [ -r /proc/1/environ ] && tr '\000' '\n' </proc/1/environ | grep -Eiq 'container=' && return 0
127 grep -qF -e /docker/ -e /libpod- /proc/self/cgroup 2>/dev/null && return 0
128 return 1
129 }
130
131 get_docker_command() {
132 if [ -x "${docker}" ]; then
133 return 0
134 elif command -v docker >/dev/null 2>&1; then
135 docker="$(command -v docker)"
136 elif command -v podman >/dev/null 2>&1; then
137 docker="$(command -v podman)"
138 else
139 error "Unable to find a usable container tool stack. I support Docker and Podman."
140 exit 1
141 fi
142 }
143
144 run_in_container() {
145 ${docker} exec "${1}" /bin/sh -c "${2}" || return 1
146 return 0
147 }
148
149 check_for_container() {
150 get_docker_command
151 ${docker} container inspect "${1}" >/dev/null 2>&1 || return 1
152 run_in_container "${1}" "[ -d \"${NETDATA_STOCK_CONFIG_DIR}\" ]" >/dev/null 2>&1 || return 1
153 return 0
154 }
155
156 handle_container() {
157 if running_in_container; then
158 return 0
159 elif [ -z "${container}" ] && [ -f "${script_dir}/.container-hostname" ]; then
160 echo >&2 "Autodetected containerized Netdata instance. Attempting to autodetect container ID."
161 possible_container="$(cat "${script_dir}/.container-hostname")"
162 if check_for_container "${possible_container}"; then
163 container="${possible_container}"
164 elif check_for_container netdata; then
165 container="netdata"
166 else
167 error "Could not autodetect container ID. It must be supplied on the command line with the --container option."
168 exit 1
169 fi
170
171 echo >&2 "Found Netdata container with ID or name ${container}"
172 elif [ -n "${container}" ]; then
173 if ! check_for_container "${container}"; then
174 error "No container with ID or name ${container} exists."
175 exit 1
176 fi
177 fi
178 }
179
180 list_files() {
181 check_directories
182 handle_container
183
184 if test -t && command -v tput > /dev/null 2>&1; then
185 width="$(tput cols)"
186 fi
187
188 if [ -z "${container}" ]; then
189 if [ "$(uname -s)" = "Linux" ]; then
190 # shellcheck disable=SC2046,SC2086
191 files="$(cd "${NETDATA_STOCK_CONFIG_DIR}" && ls ${width:+-C} ${width:+-w ${width}} $(find . -type f | cut -d '/' -f 2-))"
192 elif [ "$(uname -s)" = "FreeBSD" ]; then
193 if [ -n "${width}" ]; then
194 export COLUMNS="${width}"
195 fi
196
197 # shellcheck disable=SC2046
198 files="$(cd "${NETDATA_STOCK_CONFIG_DIR}" && ls ${width:+-C} $(find . -type f | cut -d '/' -f 2-))"
199 else
200 # shellcheck disable=SC2046
201 files="$(cd "${NETDATA_STOCK_CONFIG_DIR}" && ls $(find . -type f | cut -d '/' -f 2-))"
202 fi
203 else
204 files="$(run_in_container "${container}" "cd /usr/lib/netdata/conf.d && ls ${width:+-C} ${width:+-w ${width}} \$(find . -type f | cut -d '/' -f 2-)")"
205 fi
206
207 if [ -z "${files}" ]; then
208 error "Failed to find any configuration files."
209 exit 1
210 fi
211
212 cat <<EOF
213 The following configuration files are known to this script:
214
215 ${files}
216 EOF
217 exit 0
218 }
219
220 parse_args() {
221 while [ -n "${1}" ]; do
222 case "${1}" in
223 "--help") usage ;;
224 "--list") list_files ;;
225 "--file")
226 if [ -n "${2}" ]; then
227 file="${2}"
228 shift 1
229 else
230 error "No file specified to edit."
231 exit 1
232 fi
233 ;;
234 "--container")
235 if [ -n "${2}" ]; then
236 container="${2}"
237 shift 1
238 else
239 error "No container ID or name specified with the --container option."
240 exit 1
241 fi
242 ;;
243 "--editor")
244 if [ -n "${2}" ]; then
245 editor="${2}"
246 shift 1
247 else
248 error "No editor specified with the --editor option."
249 exit 1
250 fi
251 ;;
252 *)
253 if [ -z "${2}" ]; then
254 file="${1}"
255 else
256 error "Unrecognized option ${1}."
257 exit 1
258 fi
259 ;;
260 esac
261 shift 1
262 done
263
264 [ -z "${file}" ] && usage
265
266 absfile="$(abspath "${file}")"
267 if ! is_prefix "${script_dir}" "${absfile}"; then
268 error "${file} is not located under ${script_dir}"
269 exit 1
270 fi
271
272 file="${absfile##"${script_dir}"}"
273 }
274
275 copy_native() {
276 if [ ! -w "${NETDATA_USER_CONFIG_DIR}" ]; then
277 error "Cannot write to ${NETDATA_USER_CONFIG_DIR}!"
278 exit 1
279 fi
280
281 if [ -f "${NETDATA_STOCK_CONFIG_DIR}/${1}" ]; then
282 echo >&2 "Copying '${NETDATA_STOCK_CONFIG_DIR}/${1}' to '${NETDATA_USER_CONFIG_DIR}/${1}' ... "
283 cp -p "${NETDATA_STOCK_CONFIG_DIR}/${1}" "${NETDATA_USER_CONFIG_DIR}/${1}" || exit 1
284 else
285 echo >&2 "Creating empty '${NETDATA_USER_CONFIG_DIR}/${1}' ... "
286 touch "${NETDATA_USER_CONFIG_DIR}/${1}" || exit 1
287 fi
288 }
289
290 copy_container() {
291 if [ ! -w "${NETDATA_USER_CONFIG_DIR}" ]; then
292 error "Cannot write to ${NETDATA_USER_CONFIG_DIR}!"
293 exit 1
294 fi
295
296 if run_in_container "${container}" "[ -f \"${NETDATA_STOCK_CONFIG_DIR}/${1}\" ]"; then
297 echo >&2 "Copying '${NETDATA_STOCK_CONFIG_DIR}/${1}' to '${NETDATA_USER_CONFIG_DIR}/${1}' ... "
298 ${docker} cp -a "${container}:${NETDATA_STOCK_CONFIG_DIR}/${1}" "${NETDATA_USER_CONFIG_DIR}/${1}" || exit 1
299 else
300 echo >&2 "Creating empty '${NETDATA_USER_CONFIG_DIR}/${1}' ... "
301 touch "${NETDATA_USER_CONFIG_DIR}/${1}" || exit 1
302 fi
303 }
304
305 copy() {
306 if [ -f "${NETDATA_USER_CONFIG_DIR}/${1}" ]; then
307 return 0
308 elif [ -n "${container}" ]; then
309 copy_container "${1}"
310 else
311 copy_native "${1}"
312 fi
313 }
314
315 edit() {
316 echo >&2 "Editing '${1}' ..."
317
318 # check we can edit
319 if [ ! -w "${1}" ]; then
320 error "Cannot write to ${1}!"
321 exit 1
322 fi
323
324 "${editor}" "${1}"
325 exit $?
326 }
327
328 main() {
329 parse_args "${@}"
330 check_directories
331 check_editor
332 handle_container
333 copy "${file}"
334 edit "${absfile}"
335 }
336
337 main "${@}"