shell – How to use crontab in Android?-ThrowExceptions

Exception or error:

I can’t find answer to my question: Is it possible to run crontab to reboot Android using busybox (or other means)

Tried to run crontab, and it complain about unknown uid 0.

Tried to run reboot, and it does nothing.

Or I am asking for the impossible right now?

How to solve:

Requirements

  1. Root access: for superuser commads like reboot, or init.d config. Crond can still run under normal user privs.

  2. Busybox: for ‘crond’ service

  3. (Optional) init.d support: to start ‘crond’ service at boot. Or start via Magisk post-fs-data.d script.

Creating cronjob

Create the cronjob file in directory /data/crontab/ (it could be any accessible directory even in sdcard) with filename ‘root’.
Write your cronjob inside the file ‘root’.

echo ‘

53 * * * * reboot’ >> /data/crontab/root

Test without rebooting

Now open any terminal emulator in device and run the following commands..

su –

crond -b -c /data/crontab

Now the crond service will start, to check type…

pgrep -l crond
Or,
ps | grep crond

Start crond at boot

create a file at /system/etc/init.d with executable permission:

echo ‘

crond -b -c /data/crontab’ > /system/etc/init.d/crond

chmod +x /system/etc/init.d/crond

Example cronjobs

53 * * * * reboot

Will reboot your device on 53rd minute of every hour.

Note:
1. If you modify crontab, remember to restart crond daemon after killing the existing one.

  1. If the crond is not obeying your timezone, you might need to update the tzdata in your device.

  2. Better to test with */1 * * * * to see if it is working.

###

This is a complement to Seff’s answer above.
Couldn’t place it in a comment because it’s too long

/system/etc/init.d is not always guaranteed to work. In my case it did not. There are other methods mentioned in the following link in case this one didn’t work for you https://android.stackexchange.com/questions/6558/how-can-i-run-a-script-on-boot/196785

Even then crond didn’t run any job for me.
To debug the errors I killed the running instance pkill crond and ran it like that

crond -f -d0 -c /data/crontab/

This make crond run in forground and print all the debugging information.
When I ran it like that I got this warning

crond: crond (busybox 1.27.2-Stericson) started, log level 0
crond: ignoring file 'root' (no such user)

So I had to create a passwd file with entry for root

 echo 'root:x:0:0:root:/data:/system/bin/sh' > /system/etc/passwd                                                                                                         

Even then, it still failed with error like

crond:  job: 0 /system/bin/ls
crond: child running /bin/sh
crond: can't execute '/bin/sh' for user root

Please note that no where in my cronjob did I mentioned “/bin/sh”. This seems to be hard coded in the binary.

Next I added the following lines to my init script

/system/xbin/mount -o remount,rw /
/system/xbin/ln -s /system/bin/ /bin
/system/xbin/mount -o remount,ro /

and that’s it.
It worked fine after that

###

if you get error “read only file system”, you need to remount the /system as read-write:

mount -o rw,remount /dev/stl12 /system

When done, remount it as read-only:

mount -o ro,remount /dev/stl12 /system

###

You will likely need to set your TimeZone and add a root user. Adding a root user can be done like this:

echo "root:x:0:0::/system/etc/crontabs:/system/bin/sh" > /system/etc/passwd

Getting the right TimeZone string is odd. Set the TZ environment variable to the timezone string and make sure crond gets it. I have it under control of a GUI environment, and use the following for getting the TimeZone (may not be correct as I’ve not tested it on other timezones yet).

public static String findTZS() {
    String date = ZooGate.readShellCommand("date");
    String[] elements = date.split(" ");
    String label = elements[4];
    TimeZone tz = Calendar.getInstance().getTimeZone();
    boolean dlt = tz.useDaylightTime();
    int offset = tz.getDSTSavings()/600000;
    DateFormatSymbols dfs = DateFormatSymbols.getInstance();
    String[][] z = dfs.getZoneStrings();
    for (String[] za: z) {
        if (dlt) {
            if (za[4].equals(label)) {
                return za[2] + offset + za[4];
            } else if (za[2].equals(label)) {
                return za[2] + offset + za[4];
            }
        }
    }
    return "UTC";
}

Also be sure your crontab is named for the user (root) and also owned by that user. If you set the crontab on your internal storage so that you can edit it with Android text editors, you can make it a symlink to /data/media/0. I use /system/etc/crontabs/root -> /data/media/0/Cron/master

You use /data/media and /storage or /sdcard because the latter is a FUSE filesystem that hides the underlying Unix file system permissions and ownership, so you need to set the ownership on the real filesystem in /data.

If you have an older Android that uses Fat or YAFFS or whatever for the internal storage then you might have to keep your crontabs in /system

Beware that cron doesn’t always run on time under Android since it likes to oversleep.

Leave a Reply

Your email address will not be published. Required fields are marked *