Artifact 80c9e12fc3625ddf7f370c50a7c86053be67ee3b:
1 #!/bin/bash
2 # James Budiono 2011-2012, 2013
3 # Licens: GNU GPL Version 3 or later
4 # Load / unload SFS on the fly - this is also the system SFS loader for Fatdog64
5 #
6 # note: uses bash - we don't need speed here, we need features (uses array etc)
7 # can probably be converted to ash but I can't be bothered
8 # version 2 - supports start/stop as a service (to be run from /etc/init.d)
9 # When run in this way, supports persistence
10 # version 3 - proper recovery if we fail to load for whatever reason
11 # version 4 - canonicalise path if a parameter is passed in.
12 # version 5 - adapted to be the system SFS loader for Fatdog64, now lives in /sbin
13 # version 6 - more message, don't show base sfs
14
15 # constants and start-up variables
16 MAXLOOP=250 # highest loop device number we can use
17 RESERVED=10 # reserve 10 loop devices for filemnt and others
18 SFS_DIR=/mnt/home # where to look for sfs
19 BASE_SFS=fd64-620.sfs # don't show base sfs
20 UNLOAD_PERMANENTLY=false
21 LOAD_PERMANENTLY=false
22 LISTFILE=/etc/load_sfs
23 export SFS_DIR BASE_SFS
24
25 # boot-time configuration
26 #BOOTSTATE_PATH=/etc/BOOTSTATE
27 . $BOOTSTATE_PATH
28 [ "$BASE_SFS_PATH" ] && BASE_SFS=${BASE_SFS_PATH##*/}
29
30
31 # insertion point - below tmpfs, pup_save and pup_multi
32 INSERT_POINT=1 # tmpfs / pup_save
33 [ "$TMPFS_MOUNT" -a "$SAVEFILE_MOUNT" ] && INSERT_POINT=2 # both tmpfs & pup_save
34 [ "$MULTI_MOUNT" ] && INSERT_POINT=$(( $INSERT_POINT + 1 )) # pup_multi is another layer
35
36 ########## core load/unload functions ###########
37
38 # $@ text to display
39 function message() {
40 if [ "$DISPLAY" ]; then
41 Xdialog --infobox "$@" 0 0 10000
42 else
43 echo "$@"
44 fi
45 }
46
47 # output - FREELOOP = free loop device
48 function find_free_loop() {
49 local loops_in_use
50 for i in $(losetup-FULL -a | sed 's/:.*$//;s_/dev/loop__'); do
51 loops_in_use[$i]=$i
52 done
53 #echo ${loops_in_use[@]}
54
55 for ((i=$RESERVED; i<$MAXLOOP; i++)); do
56 if [ -z "${loops_in_use[$i]}" ]; then
57 break
58 fi
59 done
60 #echo loop device $i is free
61 FREELOOP=$i
62 }
63
64 # $1 = loop device number
65 function check_and_make_loop_device() {
66 if [ ! -b /dev/loop$1 ]; then
67 mknod /dev/loop$1 b 7 $1
68 # else
69 # echo loop device $1 already exist
70 fi
71 }
72
73 # $1 = pup_ro number
74 function check_and_make_pupro() {
75 if [ ! -d $AUFS_ROOT/pup_ro$1 ]; then
76 mkdir -p $AUFS_ROOT/pup_ro$1
77 # else
78 # echo pup_ro$1 already exist
79 fi
80 }
81
82 # $1 = path to SFS to load
83 function sfs_load() {
84 # make sure we're not loading the same sfs twice
85 if [ -z "$(losetup-FULL -a | fgrep "${1##*/}")" ]; then
86 # find free loop device, re-using what we can, if we can't, then make new loop/pup_ro mountpoint
87 # keep loop-N and pup_ro-N sync at all times
88 find_free_loop
89 check_and_make_loop_device $FREELOOP
90 check_and_make_pupro $FREELOOP
91
92 # now ready to load
93 if losetup /dev/loop$FREELOOP "$1"; then
94 if mount -o ro /dev/loop$FREELOOP $AUFS_ROOT/pup_ro$FREELOOP; then
95 if busybox mount -t aufs -o remount,ins:$INSERT_POINT:$AUFS_ROOT/pup_ro$FREELOOP=rr aufs /; then
96 message "$1 loaded successfully."
97 else
98 message "Failed to remount aufs $1"
99 umount -d $AUFS_ROOT/pup_ro$FREELOOP
100 fi
101 else
102 message "Failed to mount branch for $1"
103 losetup -d /dev/loop$FREELOOP
104 fi
105 else
106 message "Failed to assign loop devices loop$FREELOOP for $1"
107 fi
108 fi
109 }
110
111 # $1 = sfs-name to unload - we have to figure out which branch this is
112 function sfs_unload() {
113 # make sure the sfs we want to unload is loaded
114 local loopdev=$(losetup-FULL -a | grep "$1" | sed '/loop[0-9]:/ d;s_\(/dev/loop[0-9]*\):.*_\1_')
115 if [ -z $loopdev ]; then return; fi
116 local branch=$(grep -m 1 $loopdev /proc/mounts | awk '{print $2}')
117
118 if [ "$branch" ]; then
119 # now ready to unload
120 busybox mount -t aufs -o remount,del:"$branch" unionfs / &&
121 umount -d "$branch" &&
122 message "$1 unloaded successfully." || message "Unable to unload $1"
123 else
124 losetup -d $loopdev # try to delete the loop anyway
125 message "$1 is not listed in aufs branch - cannot unload."
126 fi
127 }
128
129 ############ persistence management #############
130 # $1 = path to add
131 function add_to_list() {
132 { cat $LISTFILE; echo "$1"; } | sort | uniq > $LISTFILE.tmp
133 mv $LISTFILE.tmp $LISTFILE
134 }
135
136 # $1 = filename to remove
137 function remove_from_list() {
138 if [ ! -f $LISTFILE ]; then return; fi
139 cat $LISTFILE | fgrep -v "$1" > $LISTFILE.tmp
140 mv $LISTFILE.tmp $LISTFILE
141 }
142
143 # get all sfs from the list
144 function get_list() {
145 if [ ! -f $LISTFILE ]; then return; fi
146 cat $LISTFILE | sort -r
147 }
148
149 ############ GUI related stuff and helpers ############
150
151 function list_loaded_sfs() {
152 losetup-FULL -a | sed '/loop[0-9]:/ d;s_^.*/__;s/)$//'
153 }
154 export -f list_loaded_sfs
155
156 # don't display already loaded sfs
157 function list_available_sfs() {
158 ls $SFS_DIR 2> /dev/null | grep -Fi ".sfs" | grep -Fiv "$BASE_SFS" | awk '
159 BEGIN {
160 while ("list_loaded_sfs" | getline) {
161 loaded[$0]=1
162 }
163 }
164 {
165 if (loaded[$0] == "") print $0
166 }
167 '
168 }
169 export -f list_available_sfs
170
171 function build_gui() {
172 export gui='
173 <window title="System SFS Loader">
174 <vbox>
175 <hbox>
176 <frame Load SFS>
177 <vbox>
178 <tree>
179 <variable>LOADSFS</variable>
180 <label>Available SFS</label>
181 <input>list_available_sfs</input>
182 <height>200</height><width>300</width>
183 </tree>
184 <checkbox>
185 <variable>LOAD_PERMANENTLY</variable>
186 <label>Load at every boot</label>
187 <default>'$LOAD_PERMANENTLY'</default>
188 </checkbox>
189 <button>
190 <label>Load</label>
191 </button>
192 <hbox>
193 <entry accept="directory">
194 <variable>SFS_DIR</variable>
195 <input>echo $SFS_DIR</input>
196 </entry>
197 <button>
198 <input file stock="gtk-open"></input>
199 <action type="fileselect">SFS_DIR</action>
200 </button>
201 <button>
202 <label>Refresh</label>
203 <action type="refresh">LOADSFS</action>
204 </button>
205 </hbox>
206 </vbox>
207 </frame>
208 <frame Unload SFS>
209 <vbox>
210 <tree>
211 <variable>UNLOADSFS</variable>
212 <label>SFS currenty loaded</label>
213 <input>list_loaded_sfs</input>
214 <height>200</height><width>300</width>
215 </tree>
216 <checkbox>
217 <variable>UNLOAD_PERMANENTLY</variable>
218 <label>Do not load again at next boot</label>
219 <default>'$UNLOAD_PERMANENTLY'</default>
220 </checkbox>
221 <button>
222 <label>Unload</label>
223 </button>
224 </vbox>
225 </frame>
226 </hbox>
227 <button>
228 <label>Done</label>
229 <action>exit:Done</action>
230 </button>
231 </vbox>
232 </window>
233 '
234 }
235
236 # gui for load/unload
237 function interactive() {
238 while true; do
239 build_gui
240 OUTPUT=$(gtkdialog -cp gui)
241 #echo $OUTPUT
242 eval "$OUTPUT"
243 case $EXIT in
244 Load)
245 if [ "$LOADSFS" ]; then
246 sfs_load "$SFS_DIR/$LOADSFS"
247 if [ "$LOAD_PERMANENTLY" = "true" ]; then
248 add_to_list "$SFS_DIR/$LOADSFS"
249 message "$LOADSFS will be loaded at next boot".
250 fi
251 else
252 message "You didn't choose anything to load. Please try again."
253 fi
254 ;;
255
256 Unload)
257 if [ "$UNLOADSFS" ]; then
258 sfs_unload "$UNLOADSFS"
259 if [ "$UNLOAD_PERMANENTLY" = "true" ]; then
260 remove_from_list "$UNLOADSFS"
261 message "$UNLOADSFS will be no longer be loaded at next boot".
262 fi
263 else
264 message "You didn't choose anything to unload. Please try again."
265 fi
266 ;;
267 Done|abort)
268 break
269 ;;
270 esac
271 done
272 touch /usr/share/applications/abiword.desktop #make menu refresh
273 return 0
274 }
275
276 ############## main ##############
277 if [ -z "$1" ]; then
278 # no parameter passed - use interactive gui
279 [ $(id -u) -ne 0 -a "$DISPLAY" ] && exec gtksu "System SFS Loader" $0
280 [ "$DISPLAY" ] && interactive || echo "Try ${0##*/} --help"
281 else
282 case "$1" in
283 start)
284 #echo start service - loadall
285 [ $(id -u) -ne 0 -a "$DISPLAY" ] && exec gtksu "System SFS Loader" $0 start
286 [ $(id -u) -ne 0 -a -z "$DISPLAY" ] && exec su -c "$0 start"
287 for a in $(get_list); do
288 sfs_load "$a"
289 done
290 ;;
291 stop)
292 #echo stop service - unloadall
293 [ $(id -u) -ne 0 -a "$DISPLAY" ] && exec gtksu "System SFS Loader" $0 stop
294 [ $(id -u) -ne 0 -a -z "$DISPLAY" ] && exec su -c "$0 stop"
295 for a in $(get_list); do
296 sfs_unload "${a##*/}"
297 done
298 ;;
299 --help|-h|help)
300 cat << EOF
301 Usage: $0 [start|stop|help|--help|-h|/path/to/sfsfile]
302
303 Without parameters, $0 will show the GUI.
304
305 $0 has a config file, located in $LISTFILE.
306 The config file stores the list of sfs to be automatically loaded or
307 unloaded by "start" and "stop" parameters below.
308 "Permanent" option in the GUI will update this config file.
309
310 With "start", it will load all the sfs found in the config file.
311 With "stop", it will unload all the sfs found in the config file.
312 "start" and "stop" is meant for when $0 is symlink-ed to
313 /etc/init.d as system service.
314 Any other parameter will be taken as an sfs filename to load.
315 EOF
316 ;;
317 *)
318 # anything else - assume it's path to sfs to load
319 [ $(id -u) -ne 0 -a "$DISPLAY" ] && exec gtksu "System SFS Loader" $0 $(readlink -f "$1")
320 [ $(id -u) -ne 0 -a -z "$DISPLAY" ] && exec su -c "$0 \"$(readlink -f "$1")\""
321 sfs_load $(readlink -f "$1")
322 ;;
323 esac
324 fi