summaryrefslogtreecommitdiff
path: root/zmount
blob: 42721f7a5d77082f357f86472e0858a26dfba74a (plain)
    1 #!/usr/bin/env bash
    2 # Zmount automatically configures zram devices with the ztab config file
    3 # Copyright (C)  2021  Aaron Ball <nullspoon@oper.io>
    4 #
    5 # This program is free software: you can redistribute it and/or modify
    6 # it under the terms of the GNU General Public License as published by
    7 # the Free Software Foundation, either version 3 of the License, or
    8 # (at your option) any later version.
    9 #
   10 # This program is distributed in the hope that it will be useful,
   11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 # GNU General Public License for more details.
   14 #
   15 # You should have received a copy of the GNU General Public License
   16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
   17 
   18 set -euo pipefail
   19 export IFS=$'\n\t'
   20 
   21 CONFIG='/etc/ztab'
   22 RUN='/var/run/zmount'
   23 
   24 _parseline() {
   25   local -a lineargs=()
   26   lineargs=($(printf '%s\n' "${1:?}" | tr ' ' '\n'))
   27   if [ "${#lineargs[@]}" -ne 5 ]; then
   28     printf 'ERROR: Line "%s" does not have correct columns\n' >&2
   29     return 1
   30   fi
   31   printf '%s\n' ${lineargs[@]}
   32   return 0
   33 }
   34 
   35 _setperms() {
   36   local dir="${1}"
   37   local permstr="${2}"
   38   local perm
   39   local -a keyval=()
   40 
   41   [ "${permstr}" = 'none' ] && return 0
   42 
   43   for perm in $(printf '%s\n' ${permstr} | tr ',' '\n'); do
   44     keyval=($(printf '%s\n' "${perm}" | tr '=' '\n'))
   45     if [ "${keyval[0]}" = 'user' ]; then
   46       chown "${keyval[1]}" "${dir}"
   47     elif [ "${keyval[0]}" = 'group' ]; then
   48       chgrp "${keyval[1]}" "${dir}"
   49     elif [ "${keyval[0]}" = 'mode' ]; then
   50       chmod "${keyval[1]}" "${dir}"
   51     fi
   52   done
   53 }
   54 
   55 _setup_dev() {
   56   local fs="${1}"
   57   local size="${2}"
   58   local dir="${3}"
   59   local opts="${4:-defaults}"
   60   local perms="${5:-none}"      # Default to 777 to emulate tmpfs
   61 
   62   local dev="$(zramctl -f)"
   63   local threads="$(( $(nproc) / 2 ))"
   64 
   65   [ ! -d "${RUN}" ] && mkdir -p ${RUN}
   66 
   67   # Check for existing mounts to avoid duplicates
   68   if [ "${fs}" = 'swap' ] && [ -f "${RUN}/swap" ]; then
   69     printf 'ERROR: Zswap device already detected. Skipping second.\n' >&2
   70     return 1
   71   elif [ -f "${RUN}/${dir//\//_}" ]; then
   72     printf 'ERROR: Skipping duplicate mount dir %s\n' "${dir}" >&2
   73     return 1
   74   fi
   75 
   76   # Create the zram device
   77   zramctl "${dev}" -s "${size}" -t "${threads}"
   78 
   79   if [ "${fs}" = 'swap' ]; then
   80     mkswap "${dev}" 2>&1 1>/dev/null
   81     swapon "${dev}"
   82     printf '%s\n' "${dev}" > "${RUN}/swap"
   83   else
   84     if [ "${fs}" = 'btrfs' ]; then
   85       mkfs.btrfs -q "${dev}"
   86     elif [ "${fs}" = 'ext2' ]; then
   87       mkfs.ext2 -q "${dev}"
   88     elif [ "${fs}" = 'ext3' ]; then
   89       mkfs.ext3 -q "${dev}"
   90     elif [ "${fs}" = 'ext4' ]; then
   91       mkfs.ext4 -q "${dev}"
   92     elif [ "${fs}" = 'exfat' ]; then
   93       mkfs.exfat -q "${dev}"
   94     elif [ "${fs}" = 'vfat' ]; then
   95       mkfs.vfat -q "${dev}"
   96     elif [ "${fs}" = 'xfs' ]; then
   97       mkfs.xfs -q "${dev}"
   98     else
   99       printf 'Unknown filesystem type %s\n' "${fs}"
  100       return 1
  101     fi
  102     mount -o "${opts}" "${dev}" "${dir}"
  103     printf '%s\n' "${dev}" > "${RUN}/${dir//\//_}"
  104   fi
  105 }
  106 
  107 status() { zramctl; }
  108 
  109 start() {
  110   local -a lineargs=()
  111 
  112   for line in $(grep -v '^#' /etc/ztab); do
  113     lineargs=($(_parseline "${line}"))
  114 
  115     if [ "${lineargs[0]}" = 'swap' ]; then
  116       printf 'Setting up %s zswap\n' "${lineargs[1]}"
  117     else
  118       printf 'Setting up %s zram dev on %s\n' "${lineargs[1]}" "${lineargs[2]}"
  119     fi
  120     _setup_dev ${lineargs[@]}
  121     _setperms "${lineargs[2]}" "${lineargs[4]}"
  122   done
  123 }
  124 
  125 
  126 stop() {
  127   local -a lineargs=()
  128   local fs size dir
  129 
  130   for line in $(grep -v '^#' /etc/ztab); do
  131     lineargs=($(_parseline "${line}"))
  132     fs="${lineargs[0]}"
  133     size="${lineargs[1]}"
  134     dir="${lineargs[2]}"
  135     # Ignore opts and perms column
  136 
  137     if [ "${fs}" = 'swap' ] && [ -f "${RUN}/swap" ]; then
  138       dev="$(cat ${RUN}/swap)"
  139       printf 'Stopping %s (mounted as swap)\n' "${dev}"
  140       swapoff "${dev}"
  141       rm -f "${RUN}/swap"
  142     else
  143       if [ ! -f "${RUN}/${dir//\//_}" ]; then
  144         printf '%s is already unmounted from %s\n' "${fs}" "${dir}"
  145         continue
  146       fi
  147       dev="$(cat ${RUN}/${dir//\//_})"
  148       printf 'Stopping %s (mounted on %s)\n' "${dev}" "${dir}"
  149       umount "${dir}" || :   # Ignore unmount failures
  150       rm -f "${RUN}/${dir//\//_}"
  151     fi
  152     zramctl "${dev}" -r
  153   done
  154 }
  155 
  156 
  157 main() {
  158   if [ ${UID} -ne 0 ]; then
  159     printf 'Must be run as root\n' >&2
  160     return 1
  161   fi
  162 
  163   # Make sure the zram module is loaded
  164   modprobe zram
  165 
  166   if [ "${1:-}" = 'start' ]; then
  167     start
  168   elif [ "${1:-}" = 'stop' ]; then
  169     stop
  170   elif [ "${1:-}" = 'status' ]; then
  171     status
  172   else
  173     printf 'usage: %s [start|stop|status]\n' "${0}"
  174     return 1
  175   fi
  176 }
  177 
  178 main ${@}

Generated by cgit