/* * Copyright (C) 2020 The Android Open Source Project * * Licensed 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 com.android.textclassifier.testing; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * Class for reading a process' signal masks from the /proc filesystem. Looks for the * BLOCKED, CAUGHT, IGNORED and PENDING masks from /proc/self/status, each of which is a * 64 bit bitmask with one bit per signal. * * Maintains a map from SignalMaskInfo.Type to the bitmask. The {@code isValid} method * will only return true if all 4 masks were successfully parsed. Provides lookup * methods per signal, e.g. {@code isPending(signum)} which will throw * {@code IllegalStateException} if the current data is not valid. */ public class SignalMaskInfo { private enum Type { BLOCKED("SigBlk"), CAUGHT("SigCgt"), IGNORED("SigIgn"), PENDING("SigPnd"); // The tag for this mask in /proc/self/status private final String tag; Type(String tag) { this.tag = tag + ":\t"; } public String getTag() { return tag; } public static Map parseProcinfo(String path) { Map map = new HashMap<>(); try (BufferedReader reader = new BufferedReader(new FileReader(path))) { String line; while ((line = reader.readLine()) != null) { for (Type mask : values()) { long value = mask.tryToParse(line); if (value >= 0) { map.put(mask, value); } } } } catch (NumberFormatException | IOException e) { // Ignored - the map will end up being invalid instead. } return map; } private long tryToParse(String line) { if (line.startsWith(tag)) { return Long.valueOf(line.substring(tag.length()), 16); } else { return -1; } } } private static final String PROCFS_PATH = "/proc/self/status"; private Map maskMap = null; SignalMaskInfo() { refresh(); } public void refresh() { maskMap = Type.parseProcinfo(PROCFS_PATH); } public boolean isValid() { return (maskMap != null && maskMap.size() == Type.values().length); } public boolean isCaught(int signal) { return isSignalInMask(signal, Type.CAUGHT); } public boolean isBlocked(int signal) { return isSignalInMask(signal, Type.BLOCKED); } public boolean isPending(int signal) { return isSignalInMask(signal, Type.PENDING); } public boolean isIgnored(int signal) { return isSignalInMask(signal, Type.IGNORED); } private void checkValid() { if (!isValid()) { throw new IllegalStateException(); } } private boolean isSignalInMask(int signal, Type mask) { long bit = 1L << (signal - 1); return (getSignalMask(mask) & bit) != 0; } private long getSignalMask(Type mask) { checkValid(); Long value = maskMap.get(mask); if (value == null) { throw new IllegalStateException(); } return value; } }