1

I've written a su.d script to periodically (every 4 hours) back up data from an app using a looped sleep command:

#!/system/bin/sh

(
  # Wait for boot to complete
  until [ "$(getprop sys.boot_completed)" ]
  do
    sleep 300
  done

  while true
  do
    (
      new_dir="/storage/emulated/0/temp/AppData/$(date '+%Y%m%d-%H%M')"
      mkdir -p $new_dir
      cp /data/data/com.example.app/files/*.json $new_dir

      echo "$(date '+%F %T') | app data backup OK!" >> /storage/emulated/0/su.d.log
    ) &

    sleep 14400 # 4 hours
  done
) &

In practice, the script backs up the data only after boot—not every 4 hours.

However, if I enter a remote shell via adb and leave it alone, then the data does get backed up every 4 hours.

How can I force the periodic backup without being permanently connected to a PC? (And why isn't it working as expected?)


EDITS

  1. @Irfan Latif's comment gave me the idea of trying a different interpreter (busybox ash - #!/system/xbin/sh), but the result was the same. I'll try @mirabilos's daemonise suggestion (sh -T- -c '...') next.

  2. Tried @mirabilos's daemonise suggestion with the same result: backs up data only after boot.

  3. Tried nohup:

    nohup /system/bin/sh -T- -c '...' >/dev/null 2>&1 &
    

    Same result.

  • 2
    I can't recall exactly but I had figured that out once. I think the problem was with /system/bin/sh (korn shell). It behaves unexpectedly when not connected to a terminal and forks a shell in background. For commandline, you can go for busybox cron to schedule tasks. For UI, use an app like Tasker. – Irfan Latif Feb 17 at 18:06
  • 1
    You can go for cron daemon by simply creating a crontabs file. This seems a more appropriate solution for repeated tasks rather than using shell in background. All you need to run on boot is: busybox crond -c /path/to/crontabs. If you want to stick with shell thing, use set -x and exec &> to take logs of su.d script with timestamps to find when and why shell execution fails. – Irfan Latif Feb 22 at 22:26
3

If it’s the terminal thing @IrfanLatif wrote, try this:

#!/system/bin/sh
# run a command block in the background, detached from all terminals
/system/bin/sh -T- -c '
    # Wait for boot to complete
    until [ "$(getprop sys.boot_completed)" ]; do
        sleep 300
    done

    while true; do
        (
            new_dir="/storage/emulated/0/temp/AppData/$(date '\''+%Y%m%d-%H%M'\'')"
            mkdir -p "$new_dir"
            cp /data/data/com.example.app/files/*.json "$new_dir"

            echo "$(date '\''+%F %T'\'') | app data backup OK!" >>/storage/emulated/0/su.d.log
        ) &

        sleep 14400 # 4 hours
    done
'

The -T- option tells the shell to daemon()ise, that is, replace stdin, stdout, stderr with /dev/null and double-fork the command into the background. Perhaps this helps.

I don’t have an off-hand idea why this would otherwise not work. Full disclosure: I’m the mksh developer.

1

Having no success with sleep, I ended up going the crond route, as suggested by @Irfan Latif:

  • /system/su.d/99crond (-rwx------ root root)

    #!/system/bin/sh
    
    # File: 99crond
    # Desc: Start cron daemon
    
    (
      # Wait for boot to complete
      until [ "$(getprop sys.boot_completed)" ]
      do
        sleep 2
      done
    
      # Symlink default cron dir to /data/local/crontabs
      mount -o remount,rw /
      mkdir -p /var/spool/cron
      ln -s /data/local/crontabs /var/spool/cron/crontabs
      mount -o remount,ro /
    
      crond -b
    
      echo "$(date '+%F %T') | su.d/99crond OK!" >> /storage/emulated/0/su.d.log
    ) &
    
  • /data/local/crontabs/root (-rw------- root root)

    # /data/local/crontabs/root: Android root user crontab
    
    SHELL=/system/bin/sh
    PATH=/sbin:/system/bin:/system/xbin:/data/local/bin
    
    #.--------------------------- Minute (M) (0--59)
    #|    .---------------------- Hour (H) (0--23, 0 = midnight)
    #|    |     .---------------- Day of Month (DoM) (1--31)
    #|    |     |     .---------- Month (MON) (1--12) OR jan,feb,mar, ...
    #|    |     |     |     .---- Day of Week (DoW) (0--6, Sunday = 0 or 7) OR sun,mon,tue, ...
    #|    |     |     |     |
    #M    H     DoM   MON   DoW   COMMAND
    0     */4   *     *     *     /data/local/bin/cron_backup.sh
    #
    

    FURTHER READING

  • /data/local/bin/cron_backup.sh (-rwxr-xr-x root shell)

    #!/system/bin/sh
    
    # File: cron_backup.sh
    # Desc: Back up app data (periodically with crond)
    
    new_dir="/storage/emulated/0/temp/AppData/$(date '+%Y%m%d-%H%M')"
    mkdir -p "$new_dir"
    cp /data/data/com.example.app/files/*.json "$new_dir"
    
    echo "$(date '+%F %T') | cron_backup.sh OK!" >> /storage/emulated/0/crond.log
    

As pointed out by @Ramast here, these three files alone were not quite sufficient to get crond working: /system/etc/passwd is also required for user root to be recognised, so...

mount -o remount,rw /system
echo "root:x:0:0:root:/data:/system/bin/sh" >> /system/etc/passwd
chmod 644 /system/etc/passwd
mount -o remount,ro /system

After all that is done the data gets backed up periodically as desired.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.