FORUMS
Remove All Ads from XDA

[TRICK/CWM3/EDIFY] Output to recovery UI from shell script

11,416 posts
Thanks Meter: 88,033
 
By Chainfire, Moderator Emeritus / Senior Recognized Developer - Where is my shirt? on 5th April 2011, 02:14 PM
Post Reply Email Thread
Took me a few minutes to figure this out, so I thought to share

This is taken from some scripts I use in CF-Root, you might need to change it slightly for other CWM3 kernels (like prefixing busybox to various commands, etc).

updater-script

Assumptions:
- rootfs is mounted as rw, so we can write temporary files anywhere (ram disk)
- you put a "myscript.sh" in the system folder inside the update

All this script does is extract whatever you have in update.zip/system folder to /tmp/update, and run the myscript.sh file.

Code:
ui_print("Extracting files ...");
package_extract_dir("system", "/tmp/update");
set_perm(0, 0, 0755, "/tmp/update/myscript.sh");
run_program("/tmp/update/myscript.sh");
myscript.sh

Assumptions:
- all busybox commands are symlinked in /sbin, this is usually the case for CWM3 kernels

Code:
#!/sbin/busybox sh

# get file descriptor for output
OUTFD=$(ps | grep -v "grep" | grep -o -E "update_binary(.*)" | cut -d " " -f 3);

# same as progress command in updater-script, for example:
#
# progress 0.25 10
#
# will update the next 25% of the progress bar over a period of 10 seconds

progress() {
  if [ $OUTFD != "" ]; then
    echo "progress ${1} ${2} " 1>&$OUTFD;
  fi;
}

# same as set_progress command in updater-script, for example:
#
# set_progress 0.25
#
# sets progress bar to 25%

set_progress() {
  if [ $OUTFD != "" ]; then
    echo "set_progress ${1} " 1>&$OUTFD;
  fi;
}

# same as ui_print command in updater_script, for example:
#
# ui_print "hello world!"
#
# will output "hello world!" to recovery, while
#
# ui_print
#
# outputs an empty line

ui_print() {
  if [ $OUTFD != "" ]; then
    echo "ui_print ${1} " 1>&$OUTFD;
    echo "ui_print " 1>&$OUTFD;
  else
    echo "${1}";
  fi;
}

# --- example usage ---

# empty line after "Extracting ..." from updater-script
ui_print;

# give the user some status
ui_print "doing something (1 of 4)";
# assume this won't take more than 30 seconds
progress 0.25 30;
# you'd do something useful here
sleep 15s;
# update status
ui_print "- done with something (1 of 4)";
# we're done, make sure the progress bar is at 25%
set_progress 0.25;
# empty line
ui_print;

# repeat this a few times ;)

ui_print "doing something (2 of 4)";
progress 0.25 30;
sleep 15s;
ui_print "- done with something (2 of 4)";
set_progress 0.50;
ui_print;

ui_print "doing something (3 of 4)";
progress 0.25 30;
sleep 15s;
ui_print "- done with something (3 of 4)";
set_progress 0.75;
ui_print;

ui_print "doing something (4 of 4)";
progress 0.25 30;
sleep 15s;
ui_print "- done with something (4 of 4)";
set_progress 1.00;
ui_print;

# done !
ui_print "done! rebooting!";
How, what, why ?
While updater-script is fine for a lot of things, like installing a new ROM and whatnot, anything sufficiently complicated still has to be done through shell scripts, because a great many things just cannot be easily done in edify. It's nice to be able to give the user some status when doing these operations. There are modded versions of CWM that make the same thing possible in other ways, like simply writing to STDOUT or STDERR. This requires either a custom update_binary or recovery binary, though.

This works because communication between recovery and update_binary is through a file descriptor (pipe). Recovery runs update_binary with the FD as command line parameter. Because the shell script is run as a child process of update_binary, it can write the same commands to that FD (commands recovery listens for), because child processes inherited FD numbers and access rights.

So, all the script has to do is figure out which FD to write to, and pass it the right commands. Finding the FD isn't difficult, as it is passed on the command line and so is listed in the output of ps. Some grep and cut magic retrieve it. See the OUTFD=$(...) line. The right commands are defined in the functions at the top.

Note: this is all taken from my rfs<=>ext4 conversion script for CF-Root/ext4. Slightly adjusted, hopefully it still works as expected

Enjoy!
The Following 44 Users Say Thank You to Chainfire For This Useful Post: [ View ]
 
 
6th April 2011, 01:09 AM |#2  
Tricky103's Avatar
Inactive Recognized Developer
Flag Exeter, UK
Thanks Meter: 746
 
Donate to Me
More
Thank you chainfire

Sent from my GT-I9000 using Tapatalk
9th April 2011, 05:34 PM |#3  
ragin's Avatar
Senior Member
Flag Hubli
Thanks Meter: 159
 
More
Thanks for this chainfire.
13th October 2011, 10:44 AM |#4  
Senior Member
Thanks Meter: 254
 
More
Precisely what I was looking for! You're my hero today, man!
24th November 2011, 10:24 AM |#5  
RolluS's Avatar
Senior Member
Flag Near Paris
Thanks Meter: 426
 
Donate to Me
More
Hi Chainfire

Thanks for your tricks, I'm using it for an almost bulletproof MTD flash script.

BTW, i'd like to call some set_perm commands. But set_perm isn't a recognized command for update-binary.

IDK if I'm clear.

Do you have some advise on that?
25th November 2011, 11:55 AM |#6  
RolluS's Avatar
Senior Member
Flag Near Paris
Thanks Meter: 426
 
Donate to Me
More
Quote:
Originally Posted by RolluS

BTW, i'd like to call some set_perm commands. But set_perm isn't a recognized command for update-binary.

I've sort this writing equivalent functions:

Code:
set_perm() { # same as set_perm command in updater-script, for example:
#
# set_perm 0 3003 02750 "/system/bin/netcfg"
#
# sets user:group to 0:3003 and perm to 02750 for the file /system/bin/netcfg
    $CHOWN $1:$2 $4
    $CHMOD $3 $4
}
set_perm_recursive() { # same as set_perm command in updater-script, for example:
#
# set_perm_recursive 0 2000 0755 0755 "/system/bin"
#
# sets uid:gid to 0:2000 and perm to 0755 for folders and 0755 for files recursively in /system/bin
    $CHOWN -R $1:$2 $5
	$CHMOD $3 $5
	#chmod recursive of folder
	$FIND $5/* -type d |while read folder; do
		$CHMOD $3 $folder
	done
	#chmod recursive of files
	$FIND $5/* -type f |while read file; do
		$CHMOD $4 $file
	done
}
There is no error handling (yet), so be carrefull when calling these functions
4th December 2012, 03:26 AM |#7  
mai77's Avatar
Senior Member
Thanks Meter: 582
 
More
Thumbs down no FD, no output
Quote:
Originally Posted by Chainfire

This works because communication between recovery and update_binary is through a file descriptor (pipe). Recovery runs update_binary with the FD as command line parameter. Because the shell script is run as a child process of update_binary, it can write the same commands to that FD (commands recovery listens for), because child processes inherited FD numbers and access rights.

So, all the script has to do is figure out which FD to write to, and pass it the right commands. Finding the FD isn't difficult, as it is passed on the command line and so is listed in the output of ps. Some grep and cut magic retrieve it. See the OUTFD=$(...) line.

finding the FD is difficult on an SGY phone under MT 2.0 kernel, because is is not passed as command line param and therefore not in the ps output.

can anyone give an example of a working FD ?

the expression returns "" on my SGY, and no output is readable from script.

above zip as one file with the (.*) business removed from FD expression in myscript.sh

I don't see that ps gives me a "FD" in its output. whatever that really is. /tmp/recovery.log says that /sbin/recovery has no command line params either on SGY

from recov.log:

I:Set boot command "boot-recovery"
Command: "/sbin/recovery"


there is no FD in the command line ! where is it then ?
Attached Files
File Type: zip update.zip - [Click for QR Code] (160.9 KB, 427 views)
4th December 2012, 05:35 AM |#8  
Senior Member
Thanks Meter: 713
 
Donate to Me
More
I'll test this in a bit. Nice hack b/w.
10th December 2012, 10:45 AM |#9  
mai77's Avatar
Senior Member
Thanks Meter: 582
 
More
any news?
18th January 2013, 05:15 PM |#10  
rovo89's Avatar
Senior Recognized Developer
Thanks Meter: 81,705
 
Donate to Me
More
Thanks for this trick. What I wanted to do is to redirect stdout (and stderr) of my script to the UI. I know that you can see this in "show logs", but I wanted it to display right during the installation. Here is what I came up with:

Code:
#!/sbin/busybox ash
OUTFD=$(ps | grep -v "grep" | grep -o -E "update_binary(.*)" | cut -d " " -f 3);
/sbin/busybox ash $* 2>&1 |
while read -r line
do
    echo "ui_print $line" >&$OUTFD;
    echo "ui_print " >&$OUTFD;
done
If you save this as "scripts/stdoutwrapper.sh", you can do something like this:
Code:
package_extract_dir("scripts", "/tmp/update");
set_perm(0, 0, 0755, "/tmp/update/stdoutwrapper.sh");
run_program("/tmp/update/stdoutwrapper.sh", "/tmp/update/myscript.sh", "param1", "param2");
In myscript.sh, use any shell commands. The output will be redirected to the UI. Therefore, you should be able to write scripts that work both on the command line and in recovery without changes.
Please note: As all output will be printed, the tricks with set_progress etc. don't work. It would probably be possible to use a prefix to identify commands that should be executed, not printed, so you could do e.g. "echo '<#>set_progress 0.25'".
The Following 4 Users Say Thank You to rovo89 For This Useful Post: [ View ]
29th January 2013, 10:43 AM |#11  
mai77's Avatar
Senior Member
Thanks Meter: 582
 
More
Re: [TRICK/CWM3/EDIFY] Output to recovery UI from shell script
what value does OUTFD have? find out like below:

adb shell
ps

then /sbin/recovery might have PID 2166 on SGY phone

su
ls -l --color=never /proc/2166/fd

may give you e.g.

3 -> /dev/tty0

so 3 is your OUTFD !

now grep it accordingly for the script. use MT kernel 2.0 on SGY for above capability
The Following User Says Thank You to mai77 For This Useful Post: [ View ] Gift mai77 Ad-Free
Post Reply Subscribe to Thread

Guest Quick Reply (no urls or BBcode)
Message:
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes