/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.accumulo.server.util; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import org.apache.accumulo.core.conf.AccumuloConfiguration; import org.apache.accumulo.core.conf.Property; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Sets; public class FileSystemMonitor { private static final String PROC_MOUNTS = "/proc/mounts"; private static final Logger log = LoggerFactory.getLogger(FileSystemMonitor.class); private static final String DEVICE_PREFIX = "/dev/"; private static final Set<String> ACCEPTED_FILESYSTEMS = Sets.newHashSet("ext3", "ext4", "xfs"); static class Mount { String device; String mountPoint; String filesystemType; Set<String> options; Mount(String line) { String tokens[] = line.split("\\s+"); device = tokens[0].trim(); mountPoint = tokens[1].trim(); filesystemType = tokens[2].trim().toLowerCase(); options = new HashSet<>(Arrays.asList(tokens[3].split(","))); } @Override public String toString() { StringBuilder sb = new StringBuilder(32); sb.append(device).append(" on ").append(mountPoint).append(" as ").append(filesystemType).append(" with options ").append(options); return sb.toString(); } } static List<Mount> parse(String procFile) throws IOException { FileReader fr = new FileReader(procFile); BufferedReader br = new BufferedReader(fr); try { return getMountsFromFile(br); } finally { br.close(); } } static List<Mount> getMountsFromFile(BufferedReader br) throws IOException { List<Mount> mounts = new ArrayList<>(); String line; while ((line = br.readLine()) != null) { Mount mount = new Mount(line); if (mount.device.startsWith(DEVICE_PREFIX) && ACCEPTED_FILESYSTEMS.contains(mount.filesystemType)) { log.trace("Retaining mount to check: '{}'", mount); mounts.add(mount); } else { log.trace("Ignoring mount to check: '{}'", mount); } } return mounts; } private Map<String,Boolean> readWriteFilesystems = new HashMap<>(); public FileSystemMonitor(final String procFile, long period) throws IOException { List<Mount> mounts = parse(procFile); for (Mount mount : mounts) { if (mount.options.contains("rw")) readWriteFilesystems.put(mount.mountPoint, true); else if (mount.options.contains("ro")) readWriteFilesystems.put(mount.mountPoint, false); else throw new IOException("Filesystem " + mount + " does not have ro or rw option"); } TimerTask tt = new TimerTask() { @Override public void run() { try { checkMounts(procFile); } catch (final Exception e) { Halt.halt(-42, new Runnable() { @Override public void run() { log.error("Exception while checking mount points, halting process", e); } }); } } }; // use a new Timer object instead of a shared one. // trying to avoid the case where one the timers other // task gets stuck because a FS went read only, and this task // does not execute Timer timer = new Timer("filesystem monitor timer", true); timer.schedule(tt, period, period); } protected void checkMounts(String procFile) throws Exception { List<Mount> mounts = parse(procFile); for (Mount mount : mounts) { if (!readWriteFilesystems.containsKey(mount.mountPoint)) if (mount.options.contains("rw")) readWriteFilesystems.put(mount.mountPoint, true); else if (mount.options.contains("ro")) readWriteFilesystems.put(mount.mountPoint, false); else throw new Exception("Filesystem " + mount + " does not have ro or rw option"); else if (mount.options.contains("ro") && readWriteFilesystems.get(mount.mountPoint)) throw new Exception("Filesystem " + mount.mountPoint + " switched to read only"); } } public static void start(AccumuloConfiguration conf, Property prop) { if (conf.getBoolean(prop)) { if (new File(PROC_MOUNTS).exists()) { try { new FileSystemMonitor(PROC_MOUNTS, 60000); log.info("Filesystem monitor started"); } catch (IOException e) { log.error("Failed to initialize file system monitor", e); } } else { log.info("Not monitoring filesystems, " + PROC_MOUNTS + " does not exists"); } } } }