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 ${@}
|