└── check_mountpoints.sh /check_mountpoints.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # -------------------------------------------------------------------- 4 | # **** BEGIN LICENSE BLOCK ***** 5 | # 6 | # Version: MPL 2.0 7 | # 8 | # echocat check_mountpoints.sh, Copyright (c) 2011-2021 echocat 9 | # 10 | # This Source Code Form is subject to the terms of the Mozilla Public 11 | # License, v. 2.0. If a copy of the MPL was not distributed with this 12 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 13 | # 14 | # **** END LICENSE BLOCK ***** 15 | # -------------------------------------------------------------------- 16 | 17 | # -------------------------------------------------------------------- 18 | # Check if all specified nfs/cifs/davfs mounts exist and if they are correct implemented. 19 | # That means we check /etc/fstab, the mountpoints in the filesystem and if they 20 | # are mounted. It is written for Linux, uses proc-Filesystem and was tested on 21 | # Debian, OpenSuse 10.1 10.2 10.3 11.0, SLES 10.1 11.1, RHEL/CentOS, FreeBSD and solaris 22 | # 23 | # @author: Daniel Werdermann / dwerdermann@web.de 24 | # @projectsite: https://github.com/echocat/nagios-plugin-check_mountpoints 25 | # @version: 2.7 26 | # @date: 2021-11-26 27 | # 28 | # changes 2.7 29 | # - new flag -f to check for fs type 30 | # changes 2.6 31 | # - check only dataset type filesystem on zfs 32 | # changes 2.5 33 | # - add -E flag to exclude path 34 | # - add yas3fs 35 | # changes 2.4 36 | # - add support for ext2 37 | # changes 2.3 38 | # - add support for btrfs 39 | # changes 2.2 40 | # - add support for ceph 41 | # changes 2.1 42 | # - clean output when mount point is stalled 43 | # changes 2.0 44 | # - add support for FreeBSD 45 | # - ignore trailing slashes on mounts 46 | # changes 1.22 47 | # - add support for ext3, ext4, auto 48 | # changes 1.21 49 | # - add support for lustre fs 50 | # changes 1.20 51 | # - better check on write test 52 | # changes 1.19 53 | # - for write test, use filename, which is less prone to race conditions 54 | # changes 1.18 55 | # - write check respects stale timeout now 56 | # changes 1.17 57 | # - add support for ocfs2 58 | # changes 1.16 59 | # - minor English fixes 60 | # changes 1.15 61 | # - fix bad bug in MTAB check 62 | # changes 1.14 63 | # - better support for HP-UX, Icinga 64 | # - cleanup writecheck file after check 65 | # changes 1.13 66 | # - add support for glusterfs 67 | # changes 1.12 68 | # - add LIBEXEC path for OpenCSW-installed nagios in Solaris 69 | # changes 1.11 70 | # - just update license information 71 | # changes 1.10 72 | # - new flag -w results in a write test on the mountpoint 73 | # - kernel logger logs CRITICAL check results now as CRIT 74 | # changes 1.9 75 | # - new flag -i disable check of fstab (if you use automount etc.) 76 | # changes 1.8 77 | # - fiexes for solaris support 78 | # - improved usage text 79 | # changes 1.7 80 | # - new flag -A to autoread mounts from fstab and return OK if no mounts found in fstab 81 | # changes 1.6 82 | # - new flag -a to autoread mounts from fstab and return UNKNOWN if no mounts found in fstab 83 | # - no mountpoints given returns state UNKNOWN instead of critical now 84 | # - parameter MTAB is used correctly on solaris now 85 | # - fix some minor bugs in the way variables were used 86 | # changes 1.5 87 | # - returns error, if no mountpoints given 88 | # - change help text 89 | # changes 1.4 90 | # - add support for davfs 91 | # - look for logger path via which command 92 | # - change shebang to /bin/bash 93 | # changes 1.3 94 | # - add license information 95 | # changes 1.2 96 | # - script doesnt hang on staled nfs mounts anymore 97 | # changes 1.1 98 | # - support for nfs4 99 | # changes 1.0 100 | # - support for solaris 101 | # -------------------------------------------------------------------- 102 | 103 | 104 | # -------------------------------------------------------------------- 105 | # configuration 106 | # -------------------------------------------------------------------- 107 | PROGNAME=$(basename $0) 108 | ERR_MESG=() 109 | LOGGER="`which logger` -i -p kern.warn -t" 110 | 111 | AUTO=0 112 | AUTOIGNORE=0 113 | IGNOREFSTAB=0 114 | WRITETEST=0 115 | NOAUTOCOND=1 116 | NOAUTOIGNORE=0 117 | DFARGS='' 118 | EXCLUDE=none 119 | 120 | export PATH="/bin:/usr/local/bin:/sbin:/usr/bin:/usr/sbin:/usr/sfw/bin" 121 | LIBEXEC="/opt/nagios/libexec /usr/lib64/nagios/plugins /usr/lib/nagios/plugins /usr/lib/monitoring-plugins /usr/local/nagios/libexec /usr/local/icinga/libexec /usr/local/libexec /opt/csw/libexec/nagios-plugins /opt/plugins /usr/local/libexec/nagios/" 122 | for i in ${LIBEXEC} ; do 123 | [ -r ${i}/utils.sh ] && . ${i}/utils.sh 124 | done 125 | 126 | if [ -z "$STATE_OK" ]; then 127 | echo "nagios utils.sh not found" &>/dev/stderr 128 | exit 1 129 | fi 130 | 131 | KERNEL=`uname -s` 132 | case $KERNEL in 133 | # For solaris FSF=4 MF=3 FSTAB=/etc/vfstab MTAB=/etc/mnttab gnu grep and bash required 134 | SunOS) FSF=4 135 | MF=3 136 | OF=6 137 | NOAUTOSTR=no 138 | FSTAB=/etc/vfstab 139 | MTAB=/etc/mnttab 140 | GREP=ggrep 141 | STAT=stat 142 | ;; 143 | HP-UX) FSF=3 144 | MF=2 145 | OF=4 146 | NOAUTOSTR=noauto 147 | FSTAB=/etc/fstab 148 | MTAB=/dev/mnttab 149 | GREP=grep 150 | STAT=stat 151 | ;; 152 | FreeBSD) FSF=3 153 | MF=2 154 | OF=4 155 | NOAUTOSTR=noauto 156 | FSTAB=/etc/fstab 157 | MTAB=none 158 | GREP=grep 159 | STAT=stat 160 | ;; 161 | *) FSF=3 162 | MF=2 163 | OF=4 164 | NOAUTOSTR=noauto 165 | FSTAB=/etc/fstab 166 | MTAB=/proc/mounts 167 | GREP=grep 168 | STAT=stat 169 | ;; 170 | esac 171 | 172 | # Time in seconds after which the check assumes that an NFS mount is staled, if 173 | # it does not respond. (default: 3) 174 | TIME_TILL_STALE=3 175 | 176 | # -------------------------------------------------------------------- 177 | 178 | 179 | # -------------------------------------------------------------------- 180 | # functions 181 | # -------------------------------------------------------------------- 182 | function log() { 183 | $LOGGER ${PROGNAME} "$@"; 184 | } 185 | 186 | function usage() { 187 | echo "Usage: $PROGNAME [-m FILE] \$mountpoint [\$mountpoint2 ...]" 188 | echo "Usage: $PROGNAME -h,--help" 189 | echo "Options:" 190 | echo " -m FILE Use this mtab instead (default: ${MTAB})" 191 | echo " -f FILE Use this fstab instead (default: ${FSTAB})" 192 | echo " -N NUMBER FS Field number in fstab (default: ${FSF})" 193 | echo " -M NUMBER Mount Field number in fstab (default: ${MF})" 194 | echo " -O NUMBER Option Field number in fstab (default: ${OF})" 195 | echo " -T SECONDS Responsetime at which an NFS is declared as staled (default: ${TIME_TILL_STALE})" 196 | echo " -L Allow softlinks to be accepted instead of mount points" 197 | echo " -i Ignore fstab. Do not fail just because mount is not in fstab. (default: unset)" 198 | echo " -a Autoselect mounts from fstab (default: unset)" 199 | echo " -A Autoselect from fstab. Return OK if no mounts found. (default: unset)" 200 | echo " -E PATH Use with -a or -A to exclude a path from fstab. Use '\|' between paths for multiple. (default: unset)" 201 | echo " -o When autoselecting mounts from fstab, ignore mounts having noauto flag. (default: unset)" 202 | echo " -w Writetest. Touch file \$mountpoint/.mount_test_from_\$(hostname) (default: unset)" 203 | echo " -e ARGS Extra arguments for df (default: unset)" 204 | echo " -t FS_TYPE FS Type to check for using stat. Multiple values should be separated with commas (default: unset)" 205 | echo " MOUNTPOINTS list of mountpoints to check. Ignored when -a is given" 206 | } 207 | 208 | function print_help() { 209 | echo "" 210 | usage 211 | echo "" 212 | echo "Check if nfs/cifs/davfs/zfs/btrfs mountpoints are correctly implemented and mounted." 213 | echo "" 214 | echo "This plugin is NOT developped by the Nagios Plugin group." 215 | echo "Please do not e-mail them for support on this plugin, since" 216 | echo "they won't know what you're talking about." 217 | echo "" 218 | echo "For contact info, read the plugin itself..." 219 | } 220 | 221 | # Create a temporary mtab systems that don't have such a file 222 | # Format is dev mountpoint filesystem 223 | function make_mtab() { 224 | mtab=$(mktemp) 225 | mount > $mtab 226 | sed -i '' 's/ on / /' $mtab 227 | sed -i '' 's/ (/ /' $mtab 228 | sed -i '' 's/,.*/ /' $mtab 229 | echo $mtab 230 | } 231 | 232 | 233 | # -------------------------------------------------------------------- 234 | # startup checks 235 | # -------------------------------------------------------------------- 236 | 237 | if [ $# -eq 0 ]; then 238 | usage 239 | exit $STATE_CRITICAL 240 | fi 241 | 242 | while [ "$1" != "" ] 243 | do 244 | case "$1" in 245 | -a) AUTO=1; shift;; 246 | -A) AUTO=1; AUTOIGNORE=1; shift;; 247 | -E) EXCLUDE=$2; shift 2;; 248 | -o) NOAUTOIGNORE=1; shift;; 249 | --help) print_help; exit $STATE_OK;; 250 | -h) print_help; exit $STATE_OK;; 251 | -m) MTAB=$2; shift 2;; 252 | -f) FSTAB=$2; shift 2;; 253 | -N) FSF=$2; shift 2;; 254 | -M) MF=$2; shift 2;; 255 | -O) OF=$2; shift 2;; 256 | -T) TIME_TILL_STALE=$2; shift 2;; 257 | -i) IGNOREFSTAB=1; shift;; 258 | -w) WRITETEST=1; shift;; 259 | -L) LINKOK=1; shift;; 260 | -e) DFARGS=$2; shift 2;; 261 | -t) FSTYPE=$2; shift 2;; 262 | /*) MPS="${MPS} $1"; shift;; 263 | *) usage; exit $STATE_UNKNOWN;; 264 | esac 265 | done 266 | 267 | # ZFS file system have no fstab. Make on 268 | 269 | if [ -x '/sbin/zfs' ]; then 270 | TMPTAB=$(mktemp) 271 | cat ${FSTAB} > ${TMPTAB} 272 | for DS in $(zfs list -H -o name -t filesystem); do 273 | MP=$(zfs get -H mountpoint ${DS} |awk '{print $3}') 274 | # mountpoint ~ "none|legacy|-" 275 | if [ ! -d "$MP" ]; then 276 | continue 277 | fi 278 | if [ $(zfs get -H canmount ${DS} |awk '{print $3}') == 'off' ]; then 279 | continue 280 | fi 281 | case $KERNEL in 282 | SunOS) 283 | if [ $(zfs get -H zoned ${DS} |awk '{print $3}') == 'on' ]; then 284 | continue 285 | fi 286 | ;; 287 | FreeBSD) 288 | if [ $(zfs get -H jailed ${DS} |awk '{print $3}') == 'on' ]; then 289 | continue 290 | fi 291 | ;; 292 | esac 293 | RO=$(zfs get -H readonly ${DS} |awk '($3 == "on"){print "ro"}') 294 | [ -z "$RO" ] && RO='rw' 295 | echo -e "$DS\t$MP\tzfs\t$RO\t0\t0" >> ${TMPTAB} 296 | done 297 | FSTAB=${TMPTAB} 298 | fi 299 | 300 | if [ ${AUTO} -eq 1 ]; then 301 | if [ ${NOAUTOIGNORE} -eq 1 ]; then 302 | NOAUTOCOND='!index($'${OF}',"'${NOAUTOSTR}'")' 303 | fi 304 | if [ "${EXCLUDE}" == "none" ]; then 305 | MPS=`${GREP} -v '^#' ${FSTAB} | awk '{if ('${NOAUTOCOND}'&&($'${FSF}'=="ext2" || $'${FSF}'=="ext3" || $'${FSF}'=="xfs" || $'${FSF}'=="auto" || $'${FSF}'=="ext4" || $'${FSF}'=="nfs" || $'${FSF}'=="nfs4" || $'${FSF}'=="davfs" || $'${FSF}'=="cifs" || $'${FSF}'=="fuse" || $'${FSF}'=="glusterfs" || $'${FSF}'=="ocfs2" || $'${FSF}'=="lustre" || $'${FSF}'=="ufs" || $'${FSF}'=="zfs" || $'${FSF}'=="ceph" || $'${FSF}'=="btrfs" || $'${FSF}'=="yas3fs"))print $'${MF}'}' | sed -e 's/\/$//i' | tr '\n' ' '` 306 | else 307 | MPS=`${GREP} -v '^#' ${FSTAB} | ${GREP} -v ${EXCLUDE} | awk '{if ('${NOAUTOCOND}'&&($'${FSF}'=="ext2" || $'${FSF}'=="ext3" || $'${FSF}'=="xfs" || $'${FSF}'=="auto" || $'${FSF}'=="ext4" || $'${FSF}'=="nfs" || $'${FSF}'=="nfs4" || $'${FSF}'=="davfs" || $'${FSF}'=="cifs" || $'${FSF}'=="fuse" || $'${FSF}'=="glusterfs" || $'${FSF}'=="ocfs2" || $'${FSF}'=="lustre" || $'${FSF}'=="ufs" || $'${FSF}'=="zfs" || $'${FSF}'=="ceph" || $'${FSF}'=="btrfs" || $'${FSF}'=="yas3fs"))print $'${MF}'}' | sed -e 's/\/$//i' | tr '\n' ' '` 308 | fi 309 | fi 310 | 311 | if [ -z "${MPS}" ] && [ ${AUTOIGNORE} -eq 1 ] ; then 312 | echo "OK: no external mounts were found in ${FSTAB}" 313 | exit $STATE_OK 314 | elif [ -z "${MPS}" ]; then 315 | log "ERROR: no mountpoints given!" 316 | echo "ERROR: no mountpoints given!" 317 | usage 318 | exit $STATE_UNKNOWN 319 | fi 320 | 321 | if [ ! -f /proc/mounts -a "${MTAB}" == "/proc/mounts" ]; then 322 | log "CRIT: /proc wasn't mounted!" 323 | mount -t proc proc /proc 324 | ERR_MESG[${#ERR_MESG[*]}]="CRIT: mounted /proc $?" 325 | fi 326 | 327 | if [ "${MTAB}" == "none" ]; then 328 | MTAB=$(make_mtab) 329 | fi 330 | 331 | if [ ! -e "${MTAB}" ]; then 332 | log "CRIT: ${MTAB} doesn't exist!" 333 | echo "CRIT: ${MTAB} doesn't exist!" 334 | exit $STATE_CRITICAL 335 | fi 336 | 337 | if [ -n "${FSTYPE}" ]; then 338 | # split on commas 339 | oIFS=$IFS 340 | IFS=, read -a fstypes <<<"${FSTYPE}" 341 | IFS=$oIFS 342 | fi 343 | 344 | # -------------------------------------------------------------------- 345 | # now we check if the given parameters ... 346 | # 1) ... exist in the /etc/fstab 347 | # 2) ... are mounted 348 | # 3) ... df -k gives no stale 349 | # 4) ... exist on the filesystem 350 | # 5) ... is writable (optional) 351 | # -------------------------------------------------------------------- 352 | mpidx=0 353 | for MP in ${MPS} ; do 354 | ## If its an OpenVZ Container or -a Mode is selected skip fstab check. 355 | ## -a Mode takes mounts from fstab, we do not have to check if they exist in fstab ;) 356 | if [ ! -f /proc/vz/veinfo -a ${AUTO} -ne 1 -a ${IGNOREFSTAB} -ne 1 ]; then 357 | if [ -z "$( "${GREP}" -v '^#' "${FSTAB}" | awk '$'${MF}' == "'${MP}'" {print $'${MF}'}' )" ]; then 358 | log "CRIT: ${MP} doesn't exist in /etc/fstab" 359 | ERR_MESG[${#ERR_MESG[*]}]="${MP} doesn't exist in fstab ${FSTAB}" 360 | fi 361 | fi 362 | 363 | ## check kernel mounts 364 | if [ -z "$( awk '$'${MF}' == "'${MP}'" {print $'${MF}'}' "${MTAB}" )" ]; then 365 | ## if a softlink is not an adequate replacement 366 | if [ -z "$LINKOK" -o ! -L ${MP} ]; then 367 | log "CRIT: ${MP} is not mounted" 368 | ERR_MESG[${#ERR_MESG[*]}]="${MP} is not mounted" 369 | fi 370 | fi 371 | 372 | ## check if it stales 373 | df -k ${DFARGS} ${MP} &>/dev/null & 374 | DFPID=$! 375 | disown 376 | for (( i=1 ; i<$TIME_TILL_STALE ; i++ )) ; do 377 | if ps -p $DFPID > /dev/null ; then 378 | sleep 1 379 | else 380 | break 381 | fi 382 | done 383 | if ps -p $DFPID > /dev/null ; then 384 | $(kill -s SIGTERM $DFPID &>/dev/null) 385 | ERR_MESG[${#ERR_MESG[*]}]="${MP} did not respond in $TIME_TILL_STALE sec. Seems to be stale." 386 | else 387 | ## if it not stales, check if it is a directory 388 | ISRW=0 389 | if [ ! -d ${MP} ]; then 390 | log "CRIT: ${MP} doesn't exist on filesystem" 391 | ERR_MESG[${#ERR_MESG[*]}]="${MP} doesn't exist on filesystem" 392 | ## if wanted, check if it is writable 393 | elif [ ${WRITETEST} -eq 1 ]; then 394 | ISRW=1 395 | ## in auto mode first check if it's readonly 396 | elif [ ${WRITETEST} -eq 1 ] && [ ${AUTO} -eq 1 ]; then 397 | ISRW=1 398 | for OPT in $(${GREP} -w ${MP} ${FSTAB} |awk '{print $4}'| sed -e 's/,/ /g'); do 399 | if [ "$OPT" == 'ro' ]; then 400 | ISRW=0 401 | log "CRIT: ${TOUCHFILE} is not mounted as writable." 402 | ERR_MESG[${#ERR_MESG[*]}]="Could not write in ${MP} filesystem was mounted RO." 403 | fi 404 | done 405 | fi 406 | if [ ${ISRW} -eq 1 ]; then 407 | TOUCHFILE=${MP}/.mount_test_from_$(hostname)_$(date +%Y-%m-%d--%H-%M-%S).$RANDOM.$$ 408 | touch ${TOUCHFILE} &>/dev/null & 409 | TOUCHPID=$! 410 | for (( i=1 ; i<$TIME_TILL_STALE ; i++ )) ; do 411 | if ps -p $TOUCHPID > /dev/null ; then 412 | sleep 1 413 | else 414 | break 415 | fi 416 | done 417 | if ps -p $TOUCHPID > /dev/null ; then 418 | $(kill -s SIGTERM $TOUCHPID &>/dev/null) 419 | log "CRIT: ${TOUCHFILE} is not writable." 420 | ERR_MESG[${#ERR_MESG[*]}]="Could not write in ${MP} in $TIME_TILL_STALE sec. Seems to be stale." 421 | else 422 | if [ ! -f ${TOUCHFILE} ]; then 423 | log "CRIT: ${TOUCHFILE} is not writable." 424 | ERR_MESG[${#ERR_MESG[*]}]="Could not write in ${MP}." 425 | else 426 | rm ${TOUCHFILE} &>/dev/null 427 | fi 428 | fi 429 | fi 430 | fi 431 | 432 | # Check for FS type using stat 433 | efstype=${fstypes[$mpidx]} 434 | ((mpidx++)) 435 | 436 | if [ -z "${efstype}" ] 437 | then 438 | continue 439 | fi 440 | 441 | rfstype=$(${STAT} -f --printf='%T' "${MP}") 442 | if [ $? -ne 0 ] 443 | then 444 | log "CRIT: Fail to fetch FS type for ${MP}" 445 | ERR_MESG[${#ERR_MESG[*]}]="Fail to fetch FS type for ${MP}" 446 | continue 447 | fi 448 | 449 | if [ "${rfstype}" != "${efstype}" ] 450 | then 451 | log "CRIT: Bad FS type for ${MP}" 452 | ERR_MESG[${#ERR_MESG[*]}]="Bad FS type for ${MP}. Got '${rfstype}' while '${efstype}' was expected" 453 | continue 454 | fi 455 | done 456 | 457 | # Remove temporary files 458 | if [[ "${MTAB}" =~ "/tmp" ]]; then 459 | rm -f ${MTAB} 460 | fi 461 | if [[ "${FSTAB}" =~ "/tmp" ]]; then 462 | rm -f ${FSTAB} 463 | fi 464 | 465 | if [ ${#ERR_MESG[*]} -ne 0 ]; then 466 | echo -n "CRITICAL: " 467 | for element in "${ERR_MESG[@]}"; do 468 | echo -n ${element}" ; " 469 | done 470 | echo 471 | exit $STATE_CRITICAL 472 | fi 473 | 474 | echo "OK: all mounts were found (${MPS})" 475 | exit $STATE_OK 476 | --------------------------------------------------------------------------------