Tuesday, February 8, 2011

cron on android is awesome

Cron is a popular time based job scheduler on Unix/Linux, and is super useful as you can virtually run any command or script periodically at certain times or dates.

Even though Android is based on linux, it does not run crond (cron daemon) by default, well at least not on my Droid X.  Fortunately, you can run crond with busybox with a few hacks.  It's not the prettiest hack but it works.  Here's how you do it:
  1. Make sure that your busybox supports crond.  Try typing "crond" in Terminal and see what it says.  I run busybox v1.17.1.
  2. Run your favorite command line tool telnet, Terminal, adb, etc. to run the commands below.
  3. Remount / and /system rw.
  4. Create a /etc/passwd. (crond calls getpwnam to search user db)
    echo "root:x:0:0::/data/cron:/system/bin/bash" > /etc/passwd
  5. Create a symlink for /system/bin in /bin.
    ln -s /system/bin/ /bin
  6. Create your crontab file, and call it "root" (I created /data/cron/root).  Check the example section in this article to see what a crontab file should look like: http://en.wikipedia.org/wiki/Cron
  7. Set your timezone.  I live in California so mine looks like this (apparently the new Olson style timezones do not work):
    TZ=PST8PDT
    export TZ
  8. Run crond:
    crond -c /data/cron
  9. To verify that crond is running succesfully run "pgrep crond".  If you get a number (PID) then you know it's running.
Obviously you don't want to do the above steps every time your phone boots, so you want to create an init.d script with the above commands to run at boot, or add the commands to the end of an existing init.d script.  I've added them at the end of my init.d script called /etc/init.d/99imoseyon:
# enable crond
# crond calls getpwnam (user database search)
mount -o remount,rw -t yaffs2 `grep /system /proc/mounts | cut -d' ' -f1` /system
echo "root:x:0:0::/data/cron:/system/bin/bash" > /etc/passwd
mount -o remount,ro -t yaffs2 `grep /system /proc/mounts | cut -d' ' -f1` /system
# crond has "/bin/sh" hardcoded
mount -o remount,rw rootfs /
ln -s /system/bin/ /bin
mount -o remount,ro rootfs /

# set timezone
TZ=PST8PDT
export TZ
# use /data/cron, call the crontab file "root"
crond -c /data/cron
So what can we use it for?  Lots of things.  I'm going to start using it to periodically drop page/filesystem caches, back up SMS database nightly, kill memory resident apps hourly, etc.  There are literally hundreds of use cases for android phones. Here's a sample crontab file (backups my init.d script at 8pm every night) at /data/cron/root:

0 20 * * * cp /system/etc/init.d/99imoseyon /sdcard/data/init.d
UPDATE: Added timezone step to the instructions.  Also added a command to verify that crond is running.

21 comments:

  1. For dropping page/filesystem caches I have in my /data/cron/root file

    "0 8 * * * su -c 'echo 3 > /proc/sys/vm/drop_caches;\"

    Do I need to include the:
    "bash-3.2# cat /data/cron/root" before it as well?

    Thank you for all of these blogs by the way, my droid 2 thanks you too.

    ReplyDelete
  2. Actually, cron already runs as root so you don't need the "su -c" stuff. Try:

    0 8 * * * echo 3 > /proc/sys/vm/drop_caches

    Oh, don't include the bash line. In fact let me remove that line to avoid confusion. :)

    ReplyDelete
  3. Sweet :). No more daily reboots. Also just so you know i've credited you for all of your sysctl knowledge on droidforums in the droid 2 and droid X liberty section.

    ReplyDelete
  4. oh awesome. I'm gonna check out your thread. :)

    ReplyDelete
  5. oh also i'd change your cron job to:

    sync; echo 3 > /proc/sys/vm/drop_caches

    sync before dropping caches will be more effective.

    ReplyDelete
  6. I'll add this cron schedualer to the thread soon. Its right on top in the D2 liberty section.

    ReplyDelete
  7. Would there be a command to reboot my phone every night. My phone never gets shut off, except for a reboot every morning to keep things fresh. It would be great if it would do this each night while i was asleep.

    ReplyDelete
  8. Yup this should do it (reboots 2am every morning):

    0 2 * * * reboot

    ReplyDelete
  9. Thanks for the fast response, also I was wondering i see (TZ=PST8PDT) up there, should i just use the PST difference from my EST or can i just change it to EST? seams like TZ=EST5EDT but just wanted to check first.

    ReplyDelete
  10. Janna, I only tested pst8pdt but either should work.

    ReplyDelete
  11. I've been experimenting with using cron to perform a nightly reboot, but i'm finding that it doesn't execute while the screen is locked/sleeping. Anyone happen to know if there is a way around this or is it simply a limitation?

    ReplyDelete
  12. that's odd matt. All my cron jobs execute while the screen is locked. What does your crontab file look like and what version of busybox?

    ReplyDelete
  13. After some more testing I seem to have it working now. I added "-f" to the last line of the 99imoseyon file for the following:

    busybox1180 crond -f -c /data/cron

    I may have something else installed that was keeping crond from running in the background. I'll have to look into that further. Thanks for the prompt reply.

    ReplyDelete
  14. Yeah I suspect there's something else going on. You shouldn't have to run it in the foreground but it won't hurt either. :)

    ReplyDelete
  15. Imoseyon,

    Is there archive someplace with other sample scripts to use with Cron?

    Patrick

    ReplyDelete
  16. Hmm, can not get this to work on my android. If I start crond with the most verbose loglevel it logs the following:

    crond: crond (busybox 1.15.2) started, log level 0
    crond: ignoring root

    Why is it ignoring my root file?

    Here is the file:
    # ls -la /data/cron/root
    -rw------- 1 0 0 31 Sep 7 18:30 /data/cron/root

    And here is the content:
    # more /data/cron/root
    * * * * * /data/local/rsync.sh

    ReplyDelete
  17. any other performance tweaks that can be done with cron?

    ReplyDelete
  18. This was very helpful. Finally my old Hero can be an off-the-grid webcamera.

    ReplyDelete
  19. I'm looking to use this method to set an init.d script to free RAM at 35 minute intervals. I'd like to bake the script into my AOSP build... If I'm understanding correctly, I should add the cron lines to the end of my current script, then simply add the script to the system/etc/init.d folder, correct?

    ReplyDelete
  20. I was constantly getting "run-parts no such file or directory" error messages even after correctly setting user, group and permissions (755) on the init file. Found out that it run-parts requires the correct shell "header" to be set, so here is my version of you init script enhanced with some logging:

    #!/system/bin/sh
    # enable crond
    log -p i -t crond "Starting crond..."
    # crond calls getpwnam (user database search)
    mount -o remount,rw -t yaffs2 `grep /system /proc/mounts | cut -d' ' -f1` /system
    echo "root:x:0:0::/data/cron:/system/bin/bash" > /etc/passwd
    mount -o remount,ro -t yaffs2 `grep /system /proc/mounts | cut -d' ' -f1` /system
    # crond has "/bin/sh" hardcoded
    mount -o remount,rw rootfs /
    ln -s /system/bin/ /bin
    mount -o remount,ro rootfs /
    # set timezone
    TZ=PST8PDT
    export TZ
    # use /data/cron, call the crontab file "root"
    crond -c /data/cron
    log -p i -t crond "Starting crond finished!"

    ReplyDelete