pnd_run.sh: Latest version from sebt3
[pandora-libraries.git] / testdata / scripts / pnd_run.sh
1 #!/bin/bash
2  
3 #Usage: pnd_run.sh -p your.pnd -e executeable [-a "(arguments)"] [ -s "cd to folder inside pnd"] [-b UID (name of mountpoint/pandora/appdata)] [-x close x before launching(script needs to be started with nohup for this to work]
4 # -s startdir
5 # arguments can be inside -e, -a is optional
6  
7 #/etc/sudoers needs to be adjusted if you touch any of the sudo lines
8  
9 # look at the comments in the nox part, adjust 
10 #use "lsof /usr/lib/libX11.so.6 | awk '{print $1}'| sort | uniq > whitelist" with nothing running to generate the whitelist
11  
12 #todo - no proper order
13 #validate params better
14 #make uid/pnd_name mandatory (and rename var, its confusing!)
15 #find a clean way of shutting down x without a fixed dm, mabye avoid nohup usage somehow
16 #add options to just mount iso without union and to mount the union later
17 #cleanup
18 #Rewrite! - this sucks
19
20 list_using_fs() {
21         for p in $(fuser -m $1 2>/dev/null);do ps hf $p;done
22 }
23
24 runApp() {
25         unset CURRENTSPEED
26         if ! [ -f "${MOUNTPOINT}/pandora/appdata/${PND_NAME}/cpuspeed" ]; then
27                 if [ ${cpuspeed:-$(cat /proc/pandora/cpu_mhz_max)} -gt $(cat /proc/pandora/cpu_mhz_max) ]; then 
28                    cpuselection=$(zenity --title="set cpu speed" --height=350 --list --column "id" --column "Please select" --hide-column=1 --text="$PND_NAME suggests to set the cpu speed to $cpuspeed MHz to make it run properly.\n\n Do you want to change the cpu speed? (current speed: $(cat /proc/pandora/cpu_mhz_max) MHz)\n\nWarning: Setting the clock speed above 600MHz can be unstable and it NOT recommended!" "yes" "Yes, set it to $cpuspeed MHz" "custom" "Yes, select custom value" "yessave" "Yes, set it to $cpuspeed MHz and don't ask again" "customsave" "Yes, set it to custom speed and don't ask again" "no" "No, don't change the speed" "nosave" "No, don't chage the speed and don't ask again")
29                   if [ ${cpuselection} = "yes" ]; then  
30                         CURRENTSPEED=$(cat /proc/pandora/cpu_mhz_max)
31                         sudo /usr/pandora/scripts/op_cpuspeed.sh $cpuspeed
32                   elif [ ${cpuselection} = "custom" ]; then     
33                         CURRENTSPEED=$(cat /proc/pandora/cpu_mhz_max)
34                         sudo /usr/pandora/scripts/op_cpuspeed.sh
35                   elif [ ${cpuselection} = "customsave" ]; then 
36                         CURRENTSPEED=$(cat /proc/pandora/cpu_mhz_max)
37                         sudo /usr/pandora/scripts/op_cpuspeed.sh
38                         zenity --info --title="Note" --text="Speed saved.\n\nTo re-enable this dialogue, please delete the file\n${MOUNTPOINT}/pandora/appdata/${PND_NAME}/cpuspeed"
39                         cat /proc/pandora/cpu_mhz_max > ${MOUNTPOINT}/pandora/appdata/${PND_NAME}/cpuspeed
40                  elif [ ${cpuselection} = "yessave" ]; then     
41                         CURRENTSPEED=$(cat /proc/pandora/cpu_mhz_max)
42                         cat /proc/pandora/cpu_mhz_max > /tmp/cpuspeed           
43                         zenity --info --title="Note" --text="Speed saved.\n\nTo re-enable this dialogue, please delete the file\n${MOUNTPOINT}/pandora/appdata/${PND_NAME}/cpuspeed"
44                         sudo /usr/pandora/scripts/op_cpuspeed.sh $cpuspeed
45                         cat /proc/pandora/cpu_mhz_max > ${MOUNTPOINT}/pandora/appdata/${PND_NAME}/cpuspeed
46                  elif [ ${cpuselection} = "nosave" ]; then                      
47                         zenity --info --title="Note" --text="Speed will not be changed.\n\nTo re-enable this dialogue, please delete the file\n${MOUNTPOINT}/pandora/appdata/${PND_NAME}/cpuspeed"
48                         echo 9999 > ${MOUNTPOINT}/pandora/appdata/${PND_NAME}/cpuspeed
49                 fi
50                fi
51         else
52                 cpuspeed=$(cat "${MOUNTPOINT}/pandora/appdata/${PND_NAME}/cpuspeed")
53                 if [ "$cpuspeed" -lt "1500" ]; then
54                   CURRENTSPEED=$(cat /proc/pandora/cpu_mhz_max)
55                   echo Setting to CPU-Speed $cpuspeed MHz
56                   sudo /usr/pandora/scripts/op_cpuspeed.sh $cpuspeed
57                 fi
58         fi
59
60         cd "/mnt/utmp/$PND_NAME"                # cd to union mount
61         if [ "$STARTDIR" ] && [ -d "$STARTDIR" ]; then
62                 cd "$STARTDIR";                 # cd to folder specified by the optional arg -s
63         fi
64         echo "[------------------------------]{ App start }[---------------------------------]"
65         if [ -d /mnt/utmp/$PND_NAME/lib ];then
66                 export LD_LIBRARY_PATH="/mnt/utmp/$PND_NAME/lib:${LD_LIBRARY_PATH:-"/usr/lib:/lib"}"
67         else
68                 export LD_LIBRARY_PATH="/mnt/utmp/$PND_NAME:${LD_LIBRARY_PATH:-"/usr/lib:/lib"}"
69         fi
70         if [ -d /mnt/utmp/$PND_NAME/bin ];then
71                 export PATH="/mnt/utmp/$PND_NAME/bin:${PATH:-"/usr/bin:/bin:/usr/local/bin"}"
72         fi
73         if [ -d /mnt/utmp/$PND_NAME/share ];then
74                 export XDG_DATA_DIRS="/mnt/utmp/$PND_NAME/share:$XDG_DATA_DIRS:/usr/share"
75         fi
76         export XDG_CONFIG_HOME="/mnt/utmp/$PND_NAME"
77         "./$EXENAME" $ARGUMENTS 
78                                                 # execute app with ld_lib_path set to the union mount, a bit evil but i think its a good solution
79
80         #the app could have exited now, OR it went into bg, we still need to wait in that case till it really quits!
81         PID=$(pidof -o %PPID -x \"$EXENAME\")   # get pid of app
82         while [ "$PID" ];do                     # wait till we get no pid back for tha app, again a bit ugly, but it works
83                 sleep 10s
84                 PID=`pidof -o %PPID -x \"$EXENAME\"`
85         done
86         echo "[-------------------------------]{ App end }[----------------------------------]"
87
88         if [ ! -z "$CURRENTSPEED" ]; then
89                 sudo /usr/pandora/scripts/op_cpuspeed.sh $CURRENTSPEED
90         fi
91 }
92
93 mountPnd() {
94         if [ $(id -u) -ne 0 ];then
95                 echo "sudo /usr/pandora/scripts/pnd_run.sh -m $PNDARGS"
96                 sudo /usr/pandora/scripts/pnd_run.sh -m $PNDARGS
97                 mount | grep "on /mnt/utmp/${PND_NAME} type"
98                 if [ $? -ne 0 ];then
99                         echo "The Union File-system is not mounted !"
100                         return 1
101                 fi
102                 return $?
103         fi
104         #create mountpoints, check if they exist already first to avoid annoying error messages
105         if ! [ -d "/mnt/pnd/${PND_NAME}" ]; then 
106                 mkdir -p "/mnt/pnd/${PND_NAME}"         #mountpoint for iso, ro
107         fi 
108         #writeable dir for union
109         if ! [ -d "${APPDATADIR}" ]; then 
110                 mkdir -p "${APPDATADIR}"
111                 chmod -R a+xrw "${APPDATADIR}" 2>/dev/null
112         fi
113         # create the union mountpoint
114         if ! [ -d "/mnt/utmp/${PND_NAME}" ]; then
115                 mkdir -p "/mnt/utmp/${PND_NAME}"                # union over the two
116         fi
117         #is the union already mounted? if not mount evrything, else launch the stuff
118         mount | grep "on /mnt/utmp/${PND_NAME} type"
119         if [ $? -ne 0 ];then
120                 mount | grep "on /mnt/pnd/${PND_NAME} type"
121                 if [ $? -ne 0 ];then
122                         echo not mounted on loop yet, doing so
123                         #check if pnd is already attached to loop 
124                         LOOP=$(losetup -a | grep "$PND" | tail -n1 | awk -F: '{print $1}')
125                         #check if the loop device is already mounted
126                         if ! [ -z "$LOOP" ];then
127                                 echo "Found a loop ($LOOP), using it"
128                                 loopmountedon=$( mount | grep "$(mount | grep "$LOOP" | awk '{print $3}')" | grep utmp | awk '{print $3}' )
129                         else
130                                 loopmountedon=""
131                         fi
132                         echo "LoopMountedon: $loopmountedon"
133                         if [ ! "$loopmountedon" ]; then #check if the pnd is already attached to some loop device but not used
134                                 FREELOOP=$LOOP 
135                                 #reuse existing loop
136                                 if [ ! "$LOOP" ]; then
137                                         FREELOOP=$(/sbin/losetup -f) #get first free loop device
138                                         echo $FREELOOP
139                                         if [ ! "$FREELOOP" ]; then  # no free loop device, create a new one
140                                                     #find a free loop device and use it 
141                                                     usedminor=$(/sbin/losetup -a | tail -n1)
142                                                     usedminor=${usedminor:9:1}
143                                                     echo usedminor $usedminor
144                                                     freeminor=$(($usedminor+1))
145                                                     echo freeminor $freeminor
146                                                     mknod -m777 /dev/loop$freeminor b 7 $freeminor
147                                                     FREELOOP=/dev/loop$freeminor
148                                         fi
149                                 fi
150                                 #detect fs
151
152                                 case $PND_FSTYPE in
153                                 ISO)
154                                         /sbin/losetup $FREELOOP "$PND" #attach the pnd to the loop device
155                                         mntline="mount" #setup the mountline for later
156                                         mntdev="${FREELOOP}"
157                                         #mntline="mount -o loop,mode=777 $PND /mnt/pnd/$PND_NAME"
158                                         echo "Filetype is $PND_FSTYPE";;
159                                 directory)
160                                         #we bind the folder, now it can be treated in a unified way 
161                                         #ATENTION: -o ro doesnt work for --bind at least on 25, on 26 its possible using remount, may have changed on 27
162                                         mntline="mount --bind -o ro"
163                                         mntdev="${PND}"
164                                         echo "Filetype is $PND_FSTYPE";;
165                                 Squashfs)
166                                         /sbin/losetup $FREELOOP "$PND" #attach the pnd to the loop device
167                                         mntline="mount -t squashfs"
168                                         mntdev="${FREELOOP}"
169                                         echo "Filetype is $PND_FSTYPE";;
170                                 *)
171                                         echo "error determining fs, output was $PND_FSTYPE"
172                                         exit 1;;
173                                 esac
174                                 echo "Mounting PND ($mntline) :"
175                                 $mntline "$mntdev" "/mnt/pnd/${PND_NAME}" #mount the pnd/folder
176
177                         else #the pnd is already mounted but a mount was requested with a different basename/uid, just link it there
178                                       echo $LOOP already mounted on $loopmountedon skipping losetup - putting link to old mount
179                                       #this is bullshit
180                                       rmdir "/mnt/utmp/$PND_NAME"
181                                       ln -s $loopmountedon "/mnt/utmp/$PND_NAME" 
182                         fi
183
184                         mount | grep "on /mnt/pnd/${PND_NAME} type"
185                         if [ $? -ne 0 ];then
186                                 echo "The PND File-system is not mounted ! - Union wont work anyway"
187                                 return 2
188                         fi
189                 
190                 else
191                         echo "the PND is already mounted"
192                 fi
193                 FILESYSTEM=$(mount | grep "on $MOUNTPOINT " | grep -v rootfs | awk '{print $5}' | tail -n1) #get filesystem appdata is on to determine aufs options
194                 RO=0;for o in $(mount|grep "on $MOUNTPOINT "|sed 's/.*(//;s/)$//;s/,/ /g');do [[ $o = "ro" ]]&& RO=1;done
195                 if [ $RO -eq 1 ];then
196                         echo "SD-Card is mounted Read-only !! Trying to remount RW"
197                         mount -oremount,rw $MOUNTPOINT
198                 fi
199                 echo "Filesystem is $FILESYSTEM"
200                 echo "Mounting the Union FS using ${APPDATADIR} as Write directory:"
201                 if [[ "$FILESYSTEM" = "vfat" ]]; then # use noplink on fat, dont on other fs's 
202                         #append is fucking dirty, need to clean that up
203                         echo mount -t aufs -o exec,noplink,dirs="${APPDATADIR}=rw+nolwh":"/mnt/pnd/$PND_NAME=rr$append" none "/mnt/utmp/$PND_NAME"
204                         mount -t aufs -o exec,noplink,dirs="${APPDATADIR}=rw+nolwh":"/mnt/pnd/$PND_NAME=rr$append" none "/mnt/utmp/$PND_NAME"
205                         # put union on top
206                 else
207                         mount -t aufs -o exec,dirs="${APPDATADIR}=rw+nolwh":"/mnt/pnd/$PND_NAME=rr$append" none "/mnt/utmp/$PND_NAME" 
208                         # put union on top
209                 fi
210
211                 mount | grep "on /mnt/utmp/${PND_NAME} type"
212                 if [ $? -ne 0 ];then
213                         echo "The Union File-system is not mounted !"
214                         return 1
215                 fi
216                 
217         else
218                 echo "Union already mounted"
219         fi
220 }
221
222 cleanups() {
223         #delete folders created by aufs if empty
224         rmdir -rf "${APPDATADIR}/.wh..wh.plnk" 2>/dev/null
225         rmdir -rf "${APPDATADIR}/.wh..wh..tmp" 2>/dev/null
226
227         #delete appdata folder and ancestors if _empty_
228         rmdir -p "${APPDATADIR}" 2>/dev/null
229
230         # Clean the loopback device
231         if [ $PND_FSTYPE = ISO ] || [ $PND_FSTYPE = Squashfs ]; then # check if we where running an iso, clean up loop device if we did
232                 LOOP=$(losetup -a | grep "$(basename $PND)" | tail -n1 | awk -F: '{print $1}')
233                 /sbin/losetup -d $LOOP
234                 rm $LOOP
235         fi
236
237         echo cleanup done
238 }
239
240 umountPnd() {
241         if mount | grep -q "on /mnt/pnd/${PND_NAME} type";then
242                 umount "/mnt/pnd/$PND_NAME"
243         fi
244         if ! [ -z "$(mount |grep pnd/$PND_NAME|cut -f3 -d' ')" ]; then
245                 echo umount PND failed, didnt clean up. Process still using this FS :
246                 list_using_fs "/mnt/pnd/$PND_NAME"
247         else
248                 # removing the now useless mountpoint
249                 if [ -d /mnt/pnd/$PND_NAME ];then
250                         rmdir "/mnt/pnd/$PND_NAME"
251                 fi
252
253                 # All went well, cleaning
254                 cleanups
255         fi
256 }
257
258 umountUnion() {
259         # Are we root yet ?
260         if [ $(id -u) -ne 0 ];then
261                 sudo /usr/pandora/scripts/pnd_run.sh -u $PNDARGS
262                 return $?
263         fi
264
265         # Make sure the Union FS is unmounted
266         if mount | grep -q "on /mnt/utmp/${PND_NAME} type";then
267                 umount "/mnt/utmp/$PND_NAME" #umount union
268         fi
269         if ! [ -z "$(mount |grep utmp/$PND_NAME|cut -f3 -d' ')" ]; then
270                 echo umount UNION failed, didnt clean up. Process still using this FS :
271                 list_using_fs "/mnt/utmp/$PND_NAME"
272         else
273                 # the Union is umounted, removing the now empty mountpoint
274                 if [ -d "/mnt/utmp/$PND_NAME" ];then
275                         rmdir "/mnt/utmp/$PND_NAME"
276                 elif [ -e "/mnt/utmp/$PND_NAME" ];then
277                         rm "/mnt/utmp/$PND_NAME" >/dev/null 2>&1 # as it might be a symlink
278                 fi
279                 # Try umounting the PND
280                 umountPnd
281         fi
282 }
283
284 main() {
285         if [ $nox ]; then #the app doesnt want x to run, so we kill it and restart it once the app quits
286                 if [ ! $(pidof X) ]; then 
287                         unset $nox
288                 else
289                         applist=$(lsof /usr/lib/libX11.so.6 | awk '{print $1}'| sort | uniq)
290                         whitelist=$(cat ~/pndtest/whitelist) #adjust this to a fixed whitelist, maybe in the config dir
291                         filteredlist=$(echo -e "$applist\n\n$whitelist\n\n$whitelist" | sort | uniq -u) #whitelist appended two times so those items are always removed
292                         if [ ${#filteredlist} -ge 1 ]; then
293                                 message=$(echo -e "The following applications are still running, are you sure you want to close x? \n$filteredlist")
294                                 echo -e "?ae[34me[30m?"
295                                 xmessage -center "$message", -buttons yes,no
296                                 if [ $? = 102 ]; then
297                                         exit 1
298                                 fi
299                                 sudo /etc/init.d/slim-init stop
300                                 sleep 5s
301                         else
302                                 echo -e "?ae[34me[30m?"
303                                 xmessage -center "killing x, nothing of value will be lost", -buttons ok,cancel
304                                 if [ $? = 102 ]; then
305                                         exit 1
306                                 fi
307                                 # close x now, do we want to use slim stop or just kill x?
308                                 sudo /etc/init.d/slim-init stop
309                                 sleep 5s
310                         fi
311                 fi
312         fi
313
314         case $ACTION in
315         mount)  mountPnd;;
316         umount) umountUnion;;
317         run)
318                 mountPnd
319                 if [ $? -ne 0 ];then
320                         zenity --warning --title="Mounting the PND failed" --text="Mounting the PND failed. The application wont start. Please have a look at $LOGFILE"
321                         return 3
322                 fi
323                 oPWD=$(pwd)
324                 runApp
325                 cd $oPWD
326                 umountUnion;;
327         esac
328
329
330         if [ $nox ]; then #restart x if it was killed
331                 echo "starting x in 5s"
332                 sleep 5
333                 sudo /etc/init.d/slim-init start
334         fi
335 }
336
337 showHelp() {
338         cat <<endHELP
339 Usage: pnd_run.sh -p your.pnd -e executeable [-a "(arguments)"] [ -s "cd to folder inside pnd"] [-b UID (name of mountpoint/pandora/appdata)] [-x close x before launching(script needs to be started with nohup for this to work]
340 Usage for mounting/umounting pnd_run.sh -p your.pnd -b uid -m or -u
341 endHELP
342 }
343
344 function parseArgs() {
345 ACTION=run
346 TEMP=`getopt -o d:p:e:a:b:s:m::u::n::x::j:c: -- "$@"`
347 if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
348 # Note the quotes around `$TEMP': they are essential!
349 eval set -- "$TEMP"
350 while true ; do
351         case "$1" in
352                 -p) PND="$2";shift 2;;
353                 -e) EXENAME="$2";shift 2 ;;
354                 -b) PND_NAME="$2";shift 2;;
355                 -s) STARTDIR="$2";shift 2;;
356                 -m) ACTION=mount;shift 2;;
357                 -u) ACTION=umount;shift 2;;
358                 -x) nox=1;shift 2;;
359                 -j) append="$2";shift 2;;
360                 -c) cpuspeed="$2";shift 2;;
361                 -d) APPDATASET=1;APPDATADIR="$2";shift 2;;
362                 -a)
363                         case "$2" in
364                                 "") echo "no arguments"; shift 2 ;;
365                                 *) ARGUMENTS="$2";shift 2 ;;
366                         esac ;;
367                 --) shift ; break ;;
368                 *) echo "Error while parsing arguments!"; showHelp; exit 1 ;;
369         esac
370 done
371 }
372 ######################################################################################
373 ####    Main :
374 ##
375 PNDARGS="$@"
376 parseArgs "$@"
377
378 if [ ! -e "$PND" ]; then #check if theres a pnd suplied, need to clean that up a bit more
379         echo "ERROR: selected PND($PND) file does not exist!"
380         showHelp
381         exit 1
382 fi
383
384 if [ ! "$EXENAME" ] && [[ "$ACTION" = "run" ]]; then
385         echo "ERROR: no executable name provided!"
386         showHelp
387         exit 1
388 fi
389
390 PND_FSTYPE=$(file -b "$PND" | awk '{ print $1 }')       # is -p a zip/iso or folder?
391 MOUNTPOINT=$(df "$PND" | tail -1|awk '{print $6}')      # find out on which mountpoint the pnd is
392 if [ ! -d "$MOUNTPOINT" ] || [ $MOUNTPOINT = "/" ]; then 
393         MOUNTPOINT="";
394 fi
395
396 [ ! -z $APPDATASET ] || [ -z ${MOUNTPOINT} ] && APPDATADIR=${APPDATADIR:-$(dirname $PND)/$PND_NAME}
397 APPDATADIR=${APPDATADIR:-${MOUNTPOINT}/pandora/appdata/${PND_NAME}}
398
399 LOGFILE="/tmp/pndrun_${PND_NAME}.out"
400
401 #PND_NAME really should be something sensible and somewhat unique
402 #if -b is set use that as pnd_name, else generate it from PND
403 #get basename (strip extension if file) for union mountpoints etc, maybe  this should be changed to something specified inside the xml
404 #this should probably be changed to .... something more sensible
405 #currently only everything up to the first '.' inside the filenames is used.
406 PND_NAME=${PND_NAME:-"$(basename $PND | cut -d'.' -f1)"}
407
408 if [[ $ACTION != "run" ]];then #not logging mount and umount as these are from command-line
409         main
410 elif [ $nox ]; then
411         main > $LOGFILE 2>&1 & 
412         disown
413 else
414         main > $LOGFILE 2>&1
415 fi
416