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.

andronoid
  • 41
  • 6
  • 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 '19 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 '19 at 22:26

3 Answers3

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.

mirabilos
  • 663
  • 8
  • 18
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.

andronoid
  • 41
  • 6
1

My comment does not work too. I have understood, sleep does not work then screen is off.

You should perhaps keep the system awaked for sleep command to be working.