Steal file descriptors from parent process - android

I am using an android system and have a slightly broken su: I get a root shell, but stdin/stdout is acting weird: One example being that ctrl+c kills all children of the su process. So since su gets all the input before passing it to its children, I don't think there is anything I could do to fix this without killing the su process.
I thought about writing a small c programm that does the following:
Find parent su process id and the file descriptors (for example
/dev/pts/1)
Fork new shell process with fd/0,fd/1,fd/2 pointing to
/dev/pts/1
Kill su process
Would this work? Is there maybe an easier way to stop the su binary from "intercepting" stdin/stdout?

Related

Run multiple commands at once in RootTools/RootShell?

I'm using the RootTools library, and I need to execute two commands. The first one runs a binary, the second sends SIGINT to it, to kill it.
RootTools (as far as I know) can only have one root shell open at a time, so commands can only be executed one by one. This is a problem, because I have no way to stop my binary after I've ran it.
How can I do any of the following things?
Execute two commands at once, so I can run my kill command when the binary is running
Send SIGINT to my native process some other way (e.g. with a RootTools function)
I need to use RootTools because it's the only way for me to read standard output from my program. If there's another way to do that, though, please comment.
Do you think you can concat the commands?
Let's say I want to launch a find command, but if it takes 5 seconds, I want it to stop:
find / & sleep 5 && kill $!
We can get a better suited one liner, too (i.e. ignore standard error, kill only if needed etc.).
You could also just store the PID and kill it later (be careful, if the daemon stopped to run, his PID can be reused by the OS):
run the daemon in a root shell
my-daemon >/dev/null & echo "PID: $!"
parse the output in Java and store the PID (SharedPreferences?)
var pid = outputLine.split(" ")[1]
later on, stop the daemon with a root shell
kill <pid>

Prevent Android from killing SQLite CLI when executing a long-running query

As part of a little benchmarking I'm doing for an app, I'm opening an adb shell, running the SQLite command line interface (sqlite3), and executing a long-running query to see how it performs on the device's hardware, with the installed version of SQLite.
However, SQLite keeps exiting, bringing me back to the ADB shell. The only message that's displayed is "Killed". Since I've seen SQLite report sepecific errors like "out of memory" before, I assume Android is killing the process, as opposed to SQLite encountering a problem. Assuming that is the case, is there a way to prevent the system from doing so?
I was hoping that if a watchdog process was killing the process for being non-responsive or taking up too much CPU time for too long, stop might help but it didn't.
This is roughly what I'm doing when my process gets killed, shortened up a bit for this post:
$ adb shell
root#android:/ # cd /database/location/
root#android:/database/location # sqlite3 database.db
sqlite> .output /dev/null
sqlite> .timer ON
sqlite> SELECT ... FROM ... WHERE ...;
Killed
root#android:/database/location #

Enabling logcat in init.rc

I'm appending init.rc in Android root with:
service logcat /system/bin/logcat -v long -f /mnt/sdcard/logcat.log
This solution doesn't generate any logs. The logcat.log file doesn't exist.
How can i start gathering logcat output through init.rc ?
A couple of things that could be causing problems above:
1. you defined your service to be called logcat. That looks awfully close to what might be a reserved/pre-existing name. I would choose a more distinguished service name.
2. there is no explicit start trigger for the service, hence its entirely dependent on the context in which its defined (i.e. which init phase). Pick the wrong phase (i.e. too early) and /mnt may not even exist.
3. the service will by default be running as root and thus the logcat.log file will be rw only by root. Not good to run processes as root. And not good to force readers to be root in order to read the log file.
Here the approach I've used to achieve what you're looking to do.
Problem: Ordinarily, Android log messages remain in the kernel’s (volatile) memory only and thus doesn’t survive across reboots.
Solution: To retain those log messages across reboots requires them to be written to persistent storage (i.e. the filesystem). The following code defines such a service that is started by Android during init.
Step 1, define a service that the Android init process will spawn to do this activity. This goes in init.rc.
service persistentLogging /system/bin/logcat -r 1024 -n 9 -v threadTime -f /cache/logs/log
user system
group system log
disabled
Notes about the above:
it creates a service called persistentLogging (that will be referred to in the second step below) by the start trigger.
it requests logcat to do a rolling log file (consisting of 10 files / 1Mb each) in directory - /cache/logs (i.e. log, log.1, log.2, … log.9). Adjust to suit your needs.
the service is to run as system user. This means the log file will be read+write only by system. If your app has system privileges then you’ll be able to read the log file. I’ve also defined the service to be in the log group too since that seems appropriate although since the files are not readable by group its a moot point.
the service is initially disabled. It will be started by a trigger defined below
the service is NOT oneshot. Hence, should it die, Android will attempt to restart it.
Step 2, define a trigger for starting the service. This also goes in your init.rc file.
on post-fs
mkdir /cache/logs 0775 system log
start persistentLogging
Notes about the above:
the commands are triggered during the ‘post-fs’ phase so that they occur after filesystem partitions have been mounted and when other system directories are having their permissions changed. Ideally, this service should start as late as possible because its not important or used by any other start-up activity.
the trigger first creates the target directory before starting the service. Remember the mkdir command syntax is defined by the init.rc language. In Android this syntax is not a sh syntax eventhough it looks a lot like it.
eventhough the above logging service doesn’t start until the post-fs phase of init, it will nevertheless dump all logging information since the beginning of kernel's startup as these log messages are already in the kernel buffers and this logging service is merely copying those messages to a file.
although both code fragments above ultimately need to appear in init.rc file, it is more maintainable if those additions are made to the init.${ro.hardware}.rc file defined for the device. e.g. init.freescale.rc which is automatically included by init.rc.
If we need to start the logcat and collect all the log buffers after on post-fs-data/boot completed from init.rc you can use below code in init.rc file.
on property:dev.bootcomplete=1
start logging
on post-fs-data
start logging
service logging /system/bin/logcat -b all -v threadTime -f /data/directory_name/log -r 100000 -n 9
user system
group root system log
disabled

Android sendevent is really slow - how to speed it up?

I am doing some ui automation, and I am able to store screen touches using getevent, but when I try to send this using sendevent, it takes a really long time, making it hard to actually replay the inputs.
I have already trying loading the script onto the device and running the script locally on the device (a script with a bunch of sendevent commands). But this only imporved this slightly. Is there some other way to inject these commands in a quicker way?
The handler for touch is implemented differently across devices. You should cat /proc/bus/input/devices to see where the touch handler is implemented.
You can also do adb shell getevent, interact with the device and see the output for the interface name.
The reason why your replay takes a long time is because the sendevent binary opens the interface file, writes data to it and closes it for every call to sendevent. So in theory, if you have a bunch of sendevent commands, the binary is opening the interface file, writing data and closing it for every command.
The way I've solved this issue is by re-writing the sendevent.c file under /system/core/toolbox to open the file only once during replay, writing all the data and closing it at the end of the replay. It works perfectly for me!
OK.
Instead of using the getevent/sendevent you can try direct reading from the event interface
inside adb shell try:
dd if=/dev/input/event6 of=record1 # to record
dd if=./record1 of=/dev/input/event6 #to play
However, this may run too fast...

Send Control-C to a process running trough Runtime.getRuntime().exec("su");

I'm developping an app which needs to run a command as root user so I use:
process = Runtime.getRuntime().exec("su");
Then I launch te process with:
os = new DataOutputStream(process.getOutputStream());
os.writeBytes("tcpdump\n");
When I need the process to finish os.writeBytes("exit\n"); doesn't work and process.waitFor(); get's blocked and the process doesn't finish. I need to send Control-C to the process to stop it but I don't know how I could do it.
Thanks.
Find out whether the API has a kill() method somewhere and use that method to send the SIGINT signal to the target process.
On his github, Chainfire provides a sample implementation of a Shell class that you can use to execute commands as root. The class handles all the tasks using Threads so you can be sure that the command will not block even if it does not return.
Code Snippet:
if(Shell.SU.available()){
Shell.SU.run("commandAsRoot"); //Command executed as root
else{
System.out.println("su not found");
Or if you are certain that the su binary is available, you can just run your commands (commented line) and skip the check.
Source: How-To SU
Add this at the location you want a ctrl-c signal to be issued.
Process interrupt = Runtime.getRuntime().exec("su");
ios = new DataOutputStream(interrupt.getOutputStream());
ios.writeBytes("pkill -SIGINT tcpdump");
ios.flush();
ios.close();
interrupt.waitFor();
If there are multiple processes running by the name of tcpdump and you need to be selective, find out the specific process id using pidof and grep commands. Accept the answer if it worked for you. Let me know in comments if you are facing issues.

Categories

Resources