├── Images ├── iphone-rpi-backup-blinkt-crop.png ├── rpi-iphone-diagnostics.png └── rpi-iphone-trust.jpg ├── README.md ├── etc ├── motd ├── rc.local └── udev │ └── rules.d │ └── lol.rules └── home └── pi ├── backup-iphone.py ├── backup-iphone.sh ├── blinkt.sh ├── iphone-backups └── iphone files are rsyncd here.txt ├── log.txt ├── monitor.sh ├── udev-runs-this.sh └── usr ├── src └── this is where you should git clone libimobiledevice and friends.txt └── the script backup-iphone.sh mounts the iphone in here.txt /Images/iphone-rpi-backup-blinkt-crop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinpearson/Raspberry-Pi-for-iPhone-Backup/c9089e9c7b0276a50c9a4b214558484b78dc1aa5/Images/iphone-rpi-backup-blinkt-crop.png -------------------------------------------------------------------------------- /Images/rpi-iphone-diagnostics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinpearson/Raspberry-Pi-for-iPhone-Backup/c9089e9c7b0276a50c9a4b214558484b78dc1aa5/Images/rpi-iphone-diagnostics.png -------------------------------------------------------------------------------- /Images/rpi-iphone-trust.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinpearson/Raspberry-Pi-for-iPhone-Backup/c9089e9c7b0276a50c9a4b214558484b78dc1aa5/Images/rpi-iphone-trust.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi for iPhone backup 2 | 3 | You can use a Raspberry Pi (even a tiny RPi Zero) to backup your iphone. You plug the phone into the RPi at night, and the RPi charges it and copies its photos, music, etc onto the RPi's micro-SD card. 4 | 5 | (Note: Consider having your router disallow the RPi on the WAN, since you're just using it for iphone backup.) 6 | 7 | 8 | 9 | You can show backup progress with a [Blinkt LED strip](https://www.adafruit.com/product/3195): 10 | 11 | 12 | 13 | **Note:** This project requires Python version 3.6 or later, because it uses f-strings for easy string formatting when printing messages. 14 | 15 | ## Table of Contents 16 | 17 | - [Introduction](#introduction) 18 | - [Troubleshooting tip](#troubleshooting-tip) 19 | - [0. Install files from this repo](#0-install-files-from-this-repo) 20 | - [1. Install `libimobiledevice` from source](#1-install-libimobiledevice-from-source) 21 | - [2. Plug, pair, and mount iPhone](#2-plug-pair-and-mount-iphone) 22 | - [2a. Plug in phone](#2a-plug-in-phone) 23 | - [2b. Pair phone to RPi](#2b-pair-phone-to-rpi) 24 | - [2c. Mount phone's files on RPi](#2c-mount-phones-files-on-rpi) 25 | - [2d. Back up phone to RPi](#2d-back-up-phone-to-rpi) 26 | - [2e. Unmount, Unpair, Unplug](#2e-unmount-unpair-unplug) 27 | - [3. Run the backup script](#3-run-the-backup-script) 28 | - [4. Configure udev to run the backup script upon the phone plug-in event](#4-configure-udev-to-run-the-backup-script-upon-the-phone-plug-in-event) 29 | - [4a. udev example](#4a-udev-example) 30 | - [4b. Set up our own udev rule to run backup script upon plug-in](#4b-set-up-our-own-udev-rule-to-run-backup-script-upon-plug-in) 31 | - [5. Ensuring `udev` is configured correctly at boot](#5-ensuring-udev-is-configured-correctly-at-boot) 32 | - [6. Indicate progress with Blinkt LEDs](#6-indicate-progress-with-blinkt-leds) 33 | - [Appendix: Troubleshooting](#appendix-troubleshooting) 34 | - [Appendix: Notes / Resources](#appendix-notes-and-resources) 35 | 36 | 37 | # Introduction 38 | 39 | Accessing an iPhone's data from a Raspberry Pi is harder than you'd expect. The Raspbian OS doesn't seem to support it by default: the `apt-get install` versions of the iPhone-mounting software `libimobiledevice6` and `ifuse` did not work for my phone running iOS 11.4. So I followed [samrocketman's instructions](https://gist.github.com/samrocketman/70dff6ebb18004fc37dc5e33c259a0fc) for installing `libimobiledevice` and `ifuse` from source. Here are his instructions with my additions. 40 | 41 | Also, I wanted the RPi to download the iPhone's data as soon as I plugged it in. I did not want to have to ssh into the RPi to initiate the backup. This required setting up a `udev` rule. It was hard to get it configured just right. 42 | 43 | Lastly, there is one lingering problem I haven't solved: 44 | 45 | In order to pair the phone to the RPi, the `backup-iphone.py` script (from this repo) uses the command `$ idevicepair pair`. It seems that for this command to succeed, it requires two things: 46 | 47 | 1. the phone must be unlocked 48 | 2. the phone must "Trust" the RPi 49 | 50 | So, each time you plug in the iPhone to the RPi, you should: 51 | 52 | 1. unlock the phone, 53 | 2. wait for the "Trust this computer?" dialog to appear on the phone (sometimes takes 20 seconds to appear), 54 | 3. click "Trust" and enter passcode. 55 | 4. Keep the screen unlocked for 15 seconds. 56 | 57 | After you click "Trust", it is okay for the phone to re-lock and turn off its display. 58 | 59 | For some reason the phone doesn't remember the RPi, so you must do this each time. 60 | 61 | If everything is set up correctly, the `backup-iphone.py` script will start to run as soon as you plug in the phone. It tries to pair for about 30 seconds before it gives up, so you've got 30 seconds to remember to unlock the phone and click "Trust". 62 | 63 | 64 | ## Troubleshooting tip 65 | 66 | To help debug problems, try running `monitor.sh` on a Mac that can ssh into the Pi. It opens several terminal windows on the Mac that monitor various aspects of the USB-device manager and whether the phone is plugged in and paired. 67 | 68 | (The script `monitor.sh` assumes your `~/.ssh/config` file defines an ssh alias `rpi42` for sshing into the Pi.) 69 | 70 | - `watch -d -t lsusb` tells you when the phone is plugged in 71 | - `dmesg -w` gives you kernel messages 72 | - `tail -f /var/log/syslog` watches the syslog 73 | - `journalctl -f` watches the `systemd` journal 74 | - `udevadm monitor -kup` tells you what `udev` does when you plug in the phone 75 | - `watch -d -t /home/pi/usr/bin/idevicepair list` tells you when the phone is paired 76 | - `tail -f /home/pi/log.txt` shows the log that the `/home/pi/backup-iphone.sh` script writes to 77 | 78 | 79 | 80 | If `monitor.sh` doesn't work for you, consider using this bash snippet (for Mac). It runs each of these commands in its own terminal window on the RPi over ssh. 81 | 82 | The snippet assumes your `~/.ssh/config` file defines the alias `rpi01` to let you ssh into the RPi. 83 | 84 | REPLACE_STRINGS=( \ 85 | '' \ 86 | 'dmesg -w' \ 87 | 'watch -d -t lsusb' \ 88 | 'watch -d -t /home/pi/usr/bin/idevicepair list' \ 89 | 'tail -f /var/log/syslog' \ 90 | 'tail -f /home/pi/log.txt' \ 91 | 'journalctl -f' \ 92 | 'udevadm monitor -kup' ) # no trailing newline. 93 | for cmd in "${REPLACE_STRINGS[@]}" 94 | { 95 | osascript -e "tell application \"Terminal\" to do script \"ssh -t rpi01 $cmd\"" 96 | } 97 | 98 | 99 | # 0. Install files from this repo 100 | 101 | sudo ln -s /home/pi/Raspberry-Pi-for-iPhone-Backup/etc/udev/rules.d/lol.rules /etc/udev/rules.d/lol.rules 102 | ln -s /home/pi/Raspberry-Pi-for-iPhone-Backup/home/pi/udev-runs-this.sh /home/pi/udev-runs-this.sh 103 | ln -s /home/pi/Raspberry-Pi-for-iPhone-Backup/home/pi/backup-iphone.sh /home/pi/backup-iphone.sh 104 | ln -s /home/pi/Raspberry-Pi-for-iPhone-Backup/home/pi/backup-iphone.py /home/pi/backup-iphone.py 105 | ln -s /home/pi/Raspberry-Pi-for-iPhone-Backup/home/pi/leds.pickle /home/pi/leds.pickle 106 | ln -s /home/pi/Raspberry-Pi-for-iPhone-Backup/home/pi/leds_OFF.pickle /home/pi/leds_OFF.pickle 107 | 108 | 109 | - `lol.rules` tells `udev` to run `udev-runs-this.sh` when the iPhone is plugged in 110 | - `udev.runs.this.sh` runs `backup-iphone.sh` 111 | - `backup-iphone.sh` sets up some environment variables and calls the main script `backup-iphone.py`. 112 | - `backup-iphone.py` uses `idevicepair`, `ifuse`, and `rsync` to pair with, mount, and backup the iPhone. 113 | 114 | In the next section, we build `idevicepair` and friends from source. 115 | 116 | 117 | 118 | 119 | # 1. Install `libimobiledevice` from source 120 | 121 | In this section we `git clone` the `libimobiledevice` software and friends. 122 | 123 | However, before we do that, we do a little hack to install a user named `usbmux` that is required by a subsystem within `libimobiledevice`. 124 | 125 | The `usbmuxd` daemon is part of `libimobiledevice`; it multiplexes connections over USB to an iOS device. For safety, it runs as a user named `usbmux` with certain (limited?) permissions. It expects the `usbmux` user to exist or it will fail to start. Unfortunately, when you build `usbmuxd` from source, it doesn't create the `usbmux` user. However, `apt-get install`ing the `usbmuxd` package *does* create the `usbmux` user, and `apt-get remove`ing `usbmuxd` removes the package but doesn't delete the `usbmux` user. (However, we can't simply use `apt-get`'s version of `usbmux` because the Raspbian repos have old versions of `usbmuxd` and `libimobiledevice` that won't work with iOS 11). So before we build `usbmux` from source, we use `apt-get install` then `apt-get remove` to create the `usbmux` user. 126 | 127 | See if you have the `usbmux` user: 128 | 129 | grep usb /etc/passwd 130 | 131 | If not, install `usbmuxd` with `apt-get` to force the creation of the `usbmux` user: 132 | 133 | sudo apt-get install usbmuxd 134 | 135 | Now you should have the `usbmux` user: 136 | 137 | grep usb /etc/passwd 138 | 139 | usbmux:x:112:46:usbmux daemon,,,:/var/lib/usbmux:/bin/false 140 | 141 | Remove packages that we're going to build from source: 142 | 143 | sudo apt-get remove libimobiledevice6 ifuse usbmuxd 144 | 145 | Verify you still have the `usbmux` user. 146 | 147 | **REBOOT** 148 | 149 | Now we are ready to install `libimobiledevice` and friends from source. 150 | 151 | Add to your `.bashrc` some config that lets us build code in `~/usr` : 152 | 153 | 154 | [ ! -d "$HOME/usr/src" ] && mkdir -p "$HOME/usr/src" 155 | export PKG_CONFIG_PATH="${HOME}/usr/lib/pkgconfig:${PKG_CONFIG_PATH}" 156 | export CPATH="${HOME}/usr/include:${CPATH}" 157 | 158 | export MANPATH="${HOME}/usr/share/man:${MANPATH}" 159 | 160 | export PATH="${HOME}/usr/bin:${PATH}" 161 | export LD_LIBRARY_PATH="${HOME}/usr/lib:${LD_LIBRARY_PATH}" 162 | 163 | Don't forget to 164 | 165 | source ~/.bashrc 166 | 167 | to take effect. 168 | 169 | Install packages: 170 | 171 | sudo apt-get install -y build-essential git 172 | 173 | sudo apt-get install automake libtool pkg-config libplist-dev libplist++-dev python-dev libssl-dev libusb-1.0-0-dev libfuse-dev 174 | 175 | Get source: 176 | 177 | mkdir -p ~/usr/src 178 | cd ~/usr/src 179 | for x in libplist libusbmuxd usbmuxd libimobiledevice ifuse; do git clone https://github.com/libimobiledevice/${x}.git;done 180 | 181 | Build sources (order matters): 182 | 183 | cd ~/usr/src/libplist 184 | ./autogen.sh --prefix="$HOME/usr" 185 | make && make install 186 | cd ~/usr/src/libusbmuxd 187 | ./autogen.sh --prefix="$HOME/usr" 188 | make && make install 189 | cd ~/usr/src/libimobiledevice 190 | ./autogen.sh --prefix="$HOME/usr" 191 | make && make install 192 | cd ~/usr/src/usbmuxd 193 | ./autogen.sh --prefix="$HOME/usr" 194 | make && sudo make install 195 | 196 | Can't paste subsequent lines with the first block because it'll give subsequent lines as pw attempts to `sudo`. 197 | 198 | cd ~/usr/src/ifuse 199 | ./autogen.sh --prefix="$HOME/usr" 200 | make && make install 201 | 202 | Verify `ifuse` and `idevicepair` resolve to your new versions in `~/usr/bin/`: 203 | 204 | type -P ifuse # should be /home/pi/usr/bin/ifuse 205 | type -P idevicepair # should be /home/pi/usr/bin/idevicepair 206 | 207 | 208 | **REBOOT** 209 | 210 | We're finished installing the necessary software. 211 | 212 | 213 | # 2. Plug, pair, and mount iPhone 214 | 215 | Tips: 216 | 217 | - If any of these steps fail, consider restarting `usbmuxd` and `udev` like so: 218 | 219 | sudo service usbmuxd restart 220 | sudo service udev restart 221 | 222 | - Normally, the `usbmuxd` service isn't started on boot: `sudo service usbmuxd status` shows `Active: inactive (dead)`. However, once you plug in the phone, `sudo service usbmuxd status` will show 223 | 224 | (python3) pi@rpi01:~ $ sudo service usbmuxd status 225 | ● usbmuxd.service - Socket daemon for the usbmux protocol used by Apple devices 226 | Loaded: loaded (/lib/systemd/system/usbmuxd.service; static; vendor preset: enabled) 227 | Active: active (running) since Wed 2018-07-04 07:40:06 PDT; 4s ago 228 | Docs: man:usbmuxd(8) 229 | Main PID: 968 (usbmuxd) 230 | CGroup: /system.slice/usbmuxd.service 231 | └─968 /home/pi/usr/sbin/usbmuxd --user usbmux --systemd 232 | 233 | 234 | 235 | ## 2a. Plug in phone 236 | 237 | Plug the iPhone into the RPi's USB port. (Note the RPi Zero has micro-USB ports for both power and data; plug the phone into the data port.) 238 | 239 | Running `dmesg` shows the USB driver at least recognizes it: 240 | 241 | (python3) pi@rpi01:~ $ dmesg -wH 242 | [Jul 4 07:43] dwc_otg_handle_wakeup_detected_intr lxstate = 2 243 | [ +0.506597] usb 1-1.3: new high-speed USB device number 3 using dwc_otg 244 | [ +0.133126] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8 245 | [ +0.000025] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3 246 | [ +0.000012] usb 1-1.3: Product: iPhone 247 | [ +0.000010] usb 1-1.3: Manufacturer: Apple Inc. 248 | [ +0.000010] usb 1-1.3: SerialNumber: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 249 | [ +0.258682] ipheth 1-1.3:4.2: Apple iPhone USB Ethernet device attached 250 | [ +0.011945] usbcore: registered new interface driver ipheth 251 | [ +0.348555] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready 252 | 253 | Unlock the phone and wait 10-20 secs for the phone to ask you to Trust the computer. Click "Trust" on the phone and enter passcode (or `idevicepair pair` will fail). 254 | 255 | ## 2b. Pair phone to RPi 256 | 257 | (python3) pi@rpi01:~ $ idevicepair pair 258 | SUCCESS: Paired with device XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 259 | 260 | ## 2c. Mount phone's files on RPi 261 | 262 | We'll mount the iphone at `~/usr/mnt`. 263 | 264 | mkdir -p ~/usr/mnt 265 | ifuse ~/usr/mnt 266 | 267 | Now the iPhone's files are mounted: 268 | 269 | (python3) pi@rpi01:~ $ ls ~/usr/mnt 270 | AirFair CloudAssets Downloads LoFiCloudAssets PhotoData Podcasts Purchases Recordings Vibrations 271 | Books DCIM iTunes_Control MediaAnalysis Photos PublicStaging Radio Safari 272 | 273 | ## 2d. Back up phone to RPi 274 | 275 | mkdir ~/iphone-backups 276 | rsync -v -a ~/usr/mnt ~/iphone-backups 277 | 278 | ## 2e. Unmount, Unpair, Unplug 279 | 280 | fusermount -u ~/usr/mnt 281 | idevicepair --debug unpair 282 | 283 | 284 | 285 | 286 | 287 | # 3. Run the backup script 288 | 289 | The file `/home/pi/backup-iphone.py` contains a Python3 script that detects the phone with `lsusb`, pairs the phone to the RPi with `idevicepair pair`, mounts it with `ifuse` to `/home/pi/usr/mnt/`, and copies everything (or, everything that `libimobiledevice` makes availble) to the RPi's micro-SD card at `/home/pi/iphone-backups/` with `rsync`. 290 | 291 | (Verify `backup-iphone.py` is executable.) 292 | 293 | Run `/home/pi/backup-iphone.sh`, which calls the Python script `backup-iphone.py`, and watch its logfile with `tail -f /home/pi/log.txt`. 294 | 295 | 296 | # 4. Configure udev to run the backup script upon the phone plug-in event 297 | 298 | Now we configure the RPi to run the `backup-iphone.sh` script whenever the phone plugs into the RPi. 299 | 300 | Raspbian Linux currently (July 2018) uses the `udev` system (a part of the `systemd` init system) to run user scripts when users plug in hardware devices. 301 | 302 | 303 | ## 4a. udev example 304 | 305 | When you plug in the phone, the `udev` system runs all its "rule" files in `/lib/udev/rules.d` (for system rules) and `/etc/udev/rules.d` (for user-supplied rules) and finds the file 306 | 307 | /lib/udev/rules.d/39-usbmuxd.rules 308 | 309 | which contains the line 310 | 311 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="0", OWNER="usbmux", ENV{SYSTEMD_WANTS}="usbmuxd.service" 312 | 313 | which specifes that when the iPhone's product number is added (`lsusb` shows the phone's `vendorId:productId` as `05ac:12a8`), `systemd` should execute the file 314 | 315 | /lib/systemd/system/usbmuxd.service 316 | 317 | which contains the line 318 | 319 | ExecStart=/home/pi/usr/sbin/usbmuxd --user usbmux --systemd 320 | 321 | which is the command to run the `usbmuxd` daemon. Wow! 322 | 323 | 324 | ## 4b. Set up our own udev rule to run backup script upon plug-in 325 | 326 | Make a new `udev` rules file 327 | 328 | /etc/udev/rules.d/lol.rules 329 | 330 | that contains 331 | 332 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="add", RUN+="/bin/bash /home/pi/udev-runs-this.sh" 333 | 334 | Ensure `/home/pi/udev-runs-this.sh` is executable. 335 | 336 | Restart the `udev` daemon so it picks up your new script: 337 | 338 | sudo service udev restart 339 | sudo service usbmuxd restart # for good measure 340 | 341 | Now, when the phone is plugged in, `udev` runs `/home/pi/udev-runs-this.sh`, which contains the line 342 | 343 | echo "/bin/su -c '/home/pi/backup-iphone.sh >> /home/pi/log.txt 2>&1' pi" | at now 344 | 345 | which runs the `backup-iphone.sh` script as the `pi` user. 346 | 347 | 348 | Notes: 349 | 350 | - By default the udev task is run w/ some weird user/group, so we use `su` to run `backup-iphone.sh` as the normal user `pi`. If you forget this, rsync will give weird user/group permissions to your backed-up files and even the mount folder `~/usr/mnt` will have weird permissions. 351 | 352 | - The `| at now` is a workaround for the requirement that `udev` jobs must exit quickly so as not to slow down the `udev` system. Since our backup script may run a long time, we disconnect it from `udev` by piping it to `at` which starts it in a separate (non-child?) process. 353 | 354 | - **Important**: `at` isn't in Raspbian by default so you should install it: 355 | 356 | sudo apt-get install at 357 | 358 | 359 | 360 | # 5. Ensuring `udev` is configured correctly at boot 361 | 362 | There is some problem with `usbmuxd` and `udev` not initializing after a reboot: you have to `sudo service udev restart` or the udev script `/etc/udev/rules.d/lol.rules` will fail when you plug your phone in, logging to `journalctl -f` the error 363 | 364 | Jul 07 12:34:53 rpi01 systemd-udevd[1078]: Process '/bin/bash /home/pi/udev-runs-this.sh' failed with exit code 1. 365 | 366 | To get around this, add the line 367 | 368 | service udev restart 369 | service usbmuxd restart 370 | 371 | to 372 | 373 | /etc/rc.local 374 | 375 | which causes `systemd` to restart `udev` and `usbmuxd` after every change in runlevel. This is probably overkill, but it ensures `udev` and `usbmuxd` are running correctly after a reboot. The alternative is sshing in and running `sudo service udev restart` (and also for `usbmuxd`) after a reboot manually -- ugh. 376 | 377 | 378 | 379 | # 6. Indicate progress with Blinkt LEDs 380 | 381 | 382 | 383 | Buy LED strip from Adafruit: 384 | 385 | Install Blinkt: run `home/pi/blinkt.sh` from this repo, or download it yourself: 386 | 387 | curl https://get.pimoroni.com/blinkt | bash 388 | 389 | Test: Set 0th LED to white (RGB=255,255,255), brightness=.1 (0 to 1) 390 | 391 | python2 -c 'from blinkt import set_pixel, show, clear ; import time ; set_pixel(0,255,255,255,.1) ; show() ; time.sleep(2) ; clear()' 392 | 393 | Note: Brightness has only 32 possible values, so setting brightness to anything below `1/32 = 0.03125` rounds it to 0 and turns it off. 394 | 395 | 396 | 397 | 398 | 399 | # Appendix: Troubleshooting 400 | 401 | ## Build error "No package 'libusbmuxd-2.0' found" (or libplist) when building idevicepair 402 | 403 | Running 404 | 405 | pi@rpi42:~/usr/src/libusbmuxd $ ./autogen.sh 406 | 407 | produces error: 408 | 409 | configure: error: Package requirements (libusbmuxd-2.0 >= 2.0.2) were not met: 410 | 411 | No package 'libusbmuxd-2.0' found 412 | 413 | Consider adjusting the PKG_CONFIG_PATH environment variable if you 414 | installed software in a non-standard prefix. 415 | 416 | 417 | This doesn't fix it: 418 | 419 | PKG_CONFIG_PATH=/home/pi/usr/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig: ./autogen.sh --prefix="$HOME/usr" 420 | 421 | This fixes it (was missing `libplist`!) 422 | 423 | for x in libplist libusbmuxd usbmuxd libimobiledevice ifuse; do git clone https://github.com/libimobiledevice/${x}.git;done 424 | 425 | Then later, build `libplist`: 426 | 427 | cd ~/usr/src/libplist 428 | ./autogen.sh --prefix="$HOME/usr" 429 | make && make install 430 | 431 | 432 | ## usbmuxd should've installed these 433 | 434 | The `.rules` file that `udev` runs upon iphone plug-in: 435 | 436 | (python3) pi@rpi01:~ $ cat /lib/udev/rules.d/39-usbmuxd.rules 437 | # usbmuxd (Apple Mobile Device Muxer listening on /var/run/usbmuxd) 438 | 439 | # systemd should receive all events relating to device 440 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", TAG+="systemd" 441 | 442 | # Initialize iOS devices into "deactivated" USB configuration state and activate usbmuxd 443 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="0", OWNER="usbmux", ENV{SYSTEMD_WANTS}="usbmuxd.service" 444 | 445 | # Make sure properties don't get lost when bind action is called 446 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="bind", ENV{USBMUX_SUPPORTED}="1", OWNER="usbmux", ENV{SYSTEMD_WANTS}="usbmuxd.service" 447 | 448 | # Exit usbmuxd when the last device is removed 449 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="remove", RUN+="/home/pi/usr/sbin/usbmuxd -x" 450 | 451 | Note the line `ENV{SYSTEMD_WANTS}="usbmuxd.service"`, which tells `systemd` to run: 452 | 453 | (python3) pi@rpi01:~ $ cat /lib/systemd/system/usbmuxd.service 454 | [Unit] 455 | Description=Socket daemon for the usbmux protocol used by Apple devices 456 | Documentation=man:usbmuxd(8) 457 | 458 | [Service] 459 | ExecStart=/home/pi/usr/sbin/usbmuxd --user usbmux --systemd 460 | PIDFile=/home/pi/usr/var/run/usbmuxd.pid 461 | 462 | 463 | 464 | ## debug usbmuxd with -vvv 465 | 466 | 467 | 468 | sudo /usr/local/sbin/usbmuxd -u -U usbmux -vvv -f 469 | 470 | Or: add `-vvv` to the `systemd` service that runs `usbmuxd`: 471 | 472 | 473 | (python3) pi@rpi01:~ $ cat /lib/systemd/system/usbmuxd.service 474 | [Unit] 475 | Description=Socket daemon for the usbmux protocol used by Apple devices 476 | Documentation=man:usbmuxd(8) 477 | 478 | [Service] 479 | ExecStart=/home/pi/usr/sbin/usbmuxd --user usbmux --systemd -vvv 480 | PIDFile=/home/pi/usr/var/run/usbmuxd.pid 481 | 482 | Now `usbmuxd` runs verbosely: 483 | 484 | 485 | (python3) pi@rpi01:~ $ sudo service usbmuxd start 486 | (python3) pi@rpi01:~ $ service usbmuxd status 487 | ● usbmuxd.service - Socket daemon for the usbmux protocol used by Apple devices 488 | Loaded: loaded (/lib/systemd/system/usbmuxd.service; static; vendor preset: enabled) 489 | Active: active (running) since Sat 2018-07-07 10:09:00 PDT; 6s ago 490 | Docs: man:usbmuxd(8) 491 | Main PID: 2291 (usbmuxd) 492 | CGroup: /system.slice/usbmuxd.service 493 | └─2291 /home/pi/usr/sbin/usbmuxd --user usbmux --systemd -vvv 494 | 495 | 496 | ## "libimobiledevice.so not found" error 497 | 498 | The `LD_LIBRARY_PATH` environment variable should include `/home/pi/usr/lib/`. I fixed this by putting the line 499 | 500 | export LD_LIBRARY_PATH=/home/pi/usr/lib 501 | 502 | in `backup-iphone.sh`. 503 | 504 | 505 | ## "idevicepair: command not found" error 506 | 507 | Probably `/home/pi/usr/bin` is not in the `PATH`. I fixed this by putting 508 | 509 | export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/home/pi/usr/bin 510 | 511 | in `backup-iphone.sh`. 512 | 513 | I suspect the problem occurred because my `udev-runs-this.sh` wasn't using `su pi` to run the script. Also I think there's an option to `su` to run the cmd from a login shell, which should execute `pi`'s `.bashrc` file, which we defined the env vars in earlier. 514 | 515 | ## Other "not found" or "no device or filename" errors 516 | 517 | Debug tip: in `backup-iphone.sh` and `udev-runs-this.sh`, dump ENV to log, see what user, `PATH`, `LD_LIBRARY_PATH` you have: 518 | 519 | env >> /home/pi/log.txt 520 | 521 | 522 | ## Phone doesn't charge when plugged in to RPi 523 | 524 | Problem: iphone buzzes twice and appears to not be charging. It's like it tries to negotiate with the rpi but fails so disconnects entirely. 525 | 526 | Solution: from `sudo service usbmuxd status`, it said it couldn't find a usbmux user. I apt-get installed usbmuxd to force the creation of the user, then apt-get removed it and built it again from source. 527 | 528 | 529 | ## test that udev would run `udev-runs-this.sh` 530 | 531 | Check that udev will run the `udev-runs-this.sh` script: 532 | 533 | Plug in phone 534 | 535 | sudo udevadm test --action="add" /sys/devices/platform/soc/20980000.usb/usb1/1-1/ 536 | 537 | Note: look at `journalctl -f` or `udevadm monitor -kup` when you plug in the phone to find this weird `/sys/devices/...` location where the sysfs entry for the phone lives. 538 | 539 | Verify your udev rule appears: 540 | 541 | Reading rules file: /lib/udev/rules.d/10-local-rpi.rules 542 | ... 543 | Reading rules file: /etc/udev/rules.d/lol.rules <-------- this line should be there 544 | ... 545 | run: '/bin/bash /home/pi/udev-runs-this.sh' <------ this too 546 | ... 547 | 548 | If these are present, `udev` should execute the `udev-runs-this.sh` script. 549 | 550 | 551 | ## Lots of usbmuxd clients? 552 | 553 | Seems like lots clients connecting to usbmuxd. Can't tell which one is pairing w phone after my script stops. 554 | 555 | 556 | 557 | # Appendix: Notes and Resources 558 | 559 | 560 | - https://hackaday.com/2009/09/18/how-to-write-udev-rules/ 561 | - https://askubuntu.com/questions/581810/iphone-does-not-unmount-properly-when-unplugged 562 | - https://github.com/libimobiledevice/usbmuxd/issues/26 563 | - https://raspberrypi.stackexchange.com/questions/19600/is-there-a-way-to-automatically-activate-a-script-when-a-usb-device-connects 564 | - https://unix.stackexchange.com/questions/28548/how-to-run-custom-scripts-upon-usb-device-plug-in 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | "Usbmuxd do not start when iOS device connected to USB port (udev rule added as 39-usbmuxd.rules in /lib/udev/rules.d) 574 | usbmuxd recompiled with --without-systemd fixed the issue" 575 | 576 | 577 | Let's see if that's the case for us. 578 | 579 | 580 | looks like `service usbmuxd status` shows it running after plugin phone. 581 | 582 | 583 | 584 | 585 | "1.Script was not running because it needed sudo rights to run.." 586 | 587 | pi ALL=(ALL) NOPASSWD: /home/pi/my_script.sh 588 | 589 | 590 | 591 | 592 | You'll need 5 (five) files for such a USB device as follows, simply filling in respective values : 593 | 594 | /etc/udev/rules.d/00-usb-.rules 595 | 596 | ACTION=="add", ATTRS{idVendor}=="", ATTRS{idProduct}=="", ENV{XAUTHORITY}="/home//.Xauthority", ENV{DISPLAY}=":0", OWNER="", RUN+="/usr/local/bin/usb--in_udev" 597 | ACTION=="remove", ATTRS{idVendor}=="", ATTRS{idProduct}=="", ENV{XAUTHORITY}="/home//.Xauthority", ENV{DISPLAY}=":0", OWNER="", RUN+="/usr/local/bin/usb--out_udev" 598 | 599 | (... 4 other files) 600 | 601 | 602 | 603 | 604 | Start by finding your device in lsusb. Note the ID (eg 0a81:0101) 605 | 606 | Create a new udev rules file in /etc/udev/rules.d/ via sudoedit /etc/udev/rules.d/100-mount-videos.rulesand plonk a new rule in there like this: 607 | 608 | ACTION=="add", ATTRS{idVendor}=="0a81", ATTRS{idProduct}=="0101", RUN+="/home/your_username/bin/mount_videos.sh" 609 | 610 | A simple mount command should work. You might need a sleep 5 command in there to wait for the filesystem to initialize 611 | 612 | Addition from Allan: Long running scripts might block "all further events for this or a dependent device". My Mint man page further states "Long running tasks need to be immediately detached from the event process itself." No tip is given on where to gain the skill to do this. 613 | 614 | 615 | Or `systemd`: 616 | 617 | 618 | There's much nicer solution with systemd now. You create a service which depends and is wanted by you media e.g.: /etc/systemd/system/your.service 619 | 620 | [Unit] 621 | Description=My flashdrive script trigger 622 | Requires=media-YourMediaLabel.mount 623 | After=media-YourMediaLabel.mount 624 | 625 | [Service] 626 | ExecStart=/home/you/bin/triggerScript.sh 627 | 628 | [Install] 629 | WantedBy=media-YourMediaLabel.mount 630 | 631 | Then you have to start/enable the service: 632 | 633 | sudo systemctl start your.service 634 | sudo systemctl enable your.service 635 | 636 | After mount systemd fires your trigger script. The advantage over udev rule is that the script really fires after mount, not after adding system device. 637 | 638 | Use case: I have a crypted partition which I want to backup automatically. After adding the device I have to type-in password. If I hooked the backup script to udev, the script attempts to run at the time when I'm typing password, which will fail. 639 | 640 | Resource: Scripting with udev 641 | 642 | Note: You can find your device unit with: 643 | 644 | sudo systemctl list-units -t mount 645 | 646 | 647 | 648 | 649 | 650 | These udev rules seem to have been installed in my own system in 651 | 652 | /lib/udev/rules.d/39-usbmuxd.rules 653 | 654 | 655 | # usbmuxd (Apple Mobile Device Muxer listening on /var/run/usbmuxd) 656 | 657 | # systemd should receive all events relating to device 658 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", TAG+="systemd" 659 | 660 | # Initialize iOS devices into "deactivated" USB configuration state and activate usbmuxd 661 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="0", OWNER="usbmux", @udev_activation_rule@ 662 | 663 | # Make sure properties don't get lost when bind action is called 664 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="bind", ENV{USBMUX_SUPPORTED}="1", OWNER="usbmux", @udev_activation_rule@ 665 | 666 | # Exit usbmuxd when the last device is removed 667 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="remove", RUN+="@sbindir@/usbmuxd -x" 668 | 669 | 670 | 671 | 672 | 673 | - this post was written before systemd took over 674 | 675 | Ok, I have a workaround/fix. On line 206, right before the usb_discover call, I insert sleep(5): 676 | http://cgit.sukimashita.com/usbmuxd.git/tree/src/main.c?id=1dc5437d7c25df26368dcce8db75785eb48ec6aa#n206 677 | 678 | This delays usb_discover until after the new client is detected, and everything works. This doesn't feel like the "proper" fix, but it works for now. 679 | 680 | My distribution: Ubuntu 14.04 LTS (Trusty) 681 | 682 | 683 | FYI: even a sleep(1) appears to be sufficient. 684 | 685 | someone else: 686 | 687 | I'm using systemd (required by gnome 3). On gentoo the usbmuxd package does not install any .service files. 688 | 689 | 690 | - this is strange because the install process claimed it would install systemd service rules: 691 | 692 | 693 | 694 | "Unfortunately, sudo make install is required because it needs to write to /lib/udev/rules.d and /lib/systemd/system." 695 | 696 | 697 | 698 | 699 | 700 | Tutorial: 701 | 702 | We will write our rule in the /etc/udev/rules.d/99-togglemouse.rules file with the help of our favorite text editor. A rule definition can span over multiple lines, but if that's the case, a backslash must be used before the newline character, as a line continuation, just as in shell scripts. Here is our rule: 703 | 704 | ACTION=="add" \ 705 | , ATTRS{idProduct}=="c52f" \ 706 | , ATTRS{idVendor}=="046d" \ 707 | , ENV{DISPLAY}=":0" \ 708 | , ENV{XAUTHORITY}="/run/user/1000/gdm/Xauthority" \ 709 | , RUN+="/usr/bin/xinput --disable 16" 710 | 711 | 712 | 713 | systemd docs: 714 | 715 | 716 | 717 | 718 | 719 | 720 | This guy did it with systemd: 721 | 722 | 723 | 724 | 725 | A udev issue with usbmuxd: 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | use `at now` because udev scripts are supposed to exit quickly; udev may kill them otherwise 734 | 735 | 736 | 737 | **Todo** 738 | 739 | - Light up LEDs to show me the status or errors during backup. 740 | - https://www.raspberrypi.org/forums/viewtopic.php?t=127336 741 | - https://www.jeffgeerling.com/blogs/jeff-geerling/controlling-pwr-act-leds-raspberry-pi 742 | 743 | -------------------------------------------------------------------------------- /etc/motd: -------------------------------------------------------------------------------- 1 | =================================== 2 | ------------ HEY BUDDY ------------ 3 | 4 | The RPi's time is not set right because I don't let it on the WAN. 5 | 6 | I added the command `service udev restart` to `/etc/rc.local` so that 7 | it runs after every change in runlevel, because I wanted it to run at 8 | reboot to avoid having to restart it myself upon bootup. 9 | 10 | Here was my previous solution (restart udev on reboot): 11 | 12 | After reboot, you have to `sudo service udev restart` or the udev 13 | script `/etc/udev/rules.d/lol.rules` will fail when you plug your 14 | phone in, logging the error 15 | 16 | Jul 07 12:34:53 rpi01 systemd-udevd[1078]: Process '/bin/bash 17 | /home/pi/udev-runs-this.sh' failed with exit code 1. 18 | 19 | to `journalctl -f` . 20 | 21 | 22 | --------- HAVE A NICE DAY ---------- 23 | ==================================== 24 | 25 | -------------------------------------------------------------------------------- /etc/rc.local: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # rc.local 4 | # 5 | # This script is executed at the end of each multiuser runlevel. 6 | # Make sure that the script will "exit 0" on success or any other 7 | # value on error. 8 | # 9 | # In order to enable or disable this script just change the execution 10 | # bits. 11 | # 12 | # By default this script does nothing. 13 | 14 | # Print the IP address 15 | _IP=$(hostname -I) || true 16 | if [ "$_IP" ]; then 17 | printf "My IP address is %s\n" "$_IP" 18 | fi 19 | 20 | service udev restart 21 | 22 | exit 0 23 | -------------------------------------------------------------------------------- /etc/udev/rules.d/lol.rules: -------------------------------------------------------------------------------- 1 | # run a short little program when iphone plugged in 2 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="add", ENV{SYSTEMD_WANTS}="usbmuxd.service", RUN+="/bin/bash /home/pi/udev-runs-this.sh" 3 | 4 | # Same, for when its unplugged: 5 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ACTION=="remove", ENV{SYSTEMD_WANTS}="usbmuxd.service", RUN+="/usr/bin/python3 -c 'from blinkt import set_all, show, clear; from time import sleep; set_all(255,255,255) ; show() ; sleep(1) ; clear() ; show()'" 6 | -------------------------------------------------------------------------------- /home/pi/backup-iphone.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | print('Hello from backup-iphone.py!') 4 | 5 | import blinkt 6 | blinkt.set_clear_on_exit(False) 7 | 8 | import os, time, subprocess 9 | from datetime import datetime 10 | 11 | MOUNT_DIR = '/home/pi/usr/mnt' # Phone's filesystem appears here 12 | BACKUP_DIR_BASE = '/home/pi/iphone-backups' # We backup the phone to here 13 | 14 | ############################################## 15 | 16 | def main(): 17 | print(f'{datetime.now()}: Welcome to backup-iphone.py!') 18 | 19 | if not os.path.isdir(BACKUP_DIR_BASE): 20 | print(f'No backup dir exists, creating {BACKUP_DIR_BASE} ...') 21 | os.mkdir(BACKUP_DIR_BASE) 22 | 23 | leds = LEDs() 24 | leds.test() 25 | 26 | print('Plugged in?') 27 | leds.run_task_with_lights(task = lambda: run_repeatedly(plug_in, is_plugged_in), led = 0) 28 | 29 | # Note: takes 10 sec upon plug-in for iOS to prompt for "Trust". 30 | print('Pairing...') 31 | leds.run_task_with_lights(task = lambda: run_repeatedly(pair, is_paired), led = 1) 32 | 33 | print('Mounting...') 34 | leds.run_task_with_lights(task = lambda: run_repeatedly(mount, is_mounted), led = 2) 35 | 36 | print('Backing up...') 37 | leds.run_task_with_lights(task = lambda: backup(), led = 3) 38 | 39 | print('Unmounting...') 40 | leds.run_task_with_lights(task = lambda: run_repeatedly(unmount, is_unmounted), led = 4) 41 | 42 | print('Unpairing...') 43 | leds.run_task_with_lights(task = lambda: run_repeatedly(unpair, is_unpaired), led = 5) 44 | 45 | print(f'{datetime.now()}: Bye from backup-iphone.sh!') 46 | 47 | 48 | ############################################# 49 | 50 | def run(arg_list): 51 | print('Running:') 52 | print(arg_list) 53 | p = subprocess.run( 54 | arg_list, 55 | capture_output=True, 56 | universal_newlines=True 57 | ) 58 | print(f'returncode: "{p.returncode}"') 59 | print(f'stdout: "{p.stdout}"') 60 | print(f'stderr: "{p.stderr}"') 61 | 62 | return p 63 | 64 | def run_repeatedly(f, check, imax=100, twait=2): 65 | ''' 66 | Gonna run f() until check() returns true, 67 | up to imax times, pausing twait secs after each failure. 68 | ''' 69 | 70 | if check(): 71 | print(f'Check={check.__name__} succeeded without even running f={f.__name__} once!') 72 | return 73 | 74 | i=0 75 | while i <= imax: 76 | i += 1 77 | print(f'Running f={f.__name__}, attempt {i}/{imax}...') 78 | f() 79 | print(f'Checking {check.__name__}...') 80 | if check(): 81 | print('Success!') 82 | return 83 | else: 84 | if i == imax: 85 | raise RuntimeError(f'{f.__name__} failed {imax} times! Bailing.') 86 | else: 87 | print(f'Failed. Sleeping for {twait} secs...') 88 | time.sleep(twait) 89 | raise RuntimeError("Exceeded imax! Shouldn't get here!") 90 | 91 | def plug_in(): 92 | print('Please plug in your phone!') 93 | 94 | def is_plugged_in(): 95 | p = run(['/usr/bin/lsusb']) 96 | return 'iPad' in p.stdout or 'iPhone' in p.stdout 97 | 98 | def pair(): 99 | print('Pairing...') 100 | run(['/home/pi/usr/bin/idevicepair','pair']) 101 | 102 | def is_paired(): 103 | return len(paired_devices()) > 0 104 | 105 | def paired_devices(): 106 | p = run(['/home/pi/usr/bin/idevicepair','list']) 107 | devs = [d.strip() for d in p.stdout.split() if len(d.strip())>0] 108 | return devs 109 | 110 | def backup(): 111 | sn = phone_serial_number() 112 | print(f'Found phone serial number: {sn}') 113 | backup_dir = os.path.join(BACKUP_DIR_BASE, sn) 114 | if not os.path.isdir(backup_dir): 115 | print(f'No backup path exists, creating {backup_dir} ...') 116 | os.mkdir(backup_dir) 117 | run(['/usr/bin/rsync', '-v', '-a', MOUNT_DIR, backup_dir]) 118 | 119 | def mount(): 120 | run(['/home/pi/usr/bin/ifuse', MOUNT_DIR]) 121 | 122 | def is_mounted(): 123 | return any(MOUNT_DIR+' ' in m for m in open('/proc/mounts','r').readlines()) 124 | 125 | def unmount(): 126 | run(['/bin/fusermount', '-u', MOUNT_DIR]) 127 | 128 | def is_unmounted(): 129 | return not is_mounted() 130 | 131 | def unpair(): 132 | run(['/home/pi/usr/bin/idevicepair','unpair']) 133 | 134 | def is_unpaired(): 135 | return not is_paired() 136 | 137 | def phone_serial_number(): 138 | if not is_paired(): 139 | raise RuntimeError("Uh oh - there's no paired devices, so I can't get the phone serial #!") 140 | 141 | devs = paired_devices() 142 | 143 | if len(devs) > 1: 144 | raise RuntimeError(f'Uh oh - multiple paired devices!: {devs}') 145 | 146 | return devs[0] 147 | 148 | ######################################## 149 | 150 | class LEDs: 151 | ''' 152 | When a task begins, set to BLUE. 153 | When a task completes, set to GREEN. 154 | If a task errors, set to RED. 155 | ''' 156 | 157 | def run_task_with_lights(self, task, led): 158 | if led < 0 or led > 7: 159 | raise RuntimeError(f'Blinkt LED strip has only 8 LEDs, so led={led} should be 0,1,...,7!') 160 | 161 | self._begin_task(led) 162 | task() 163 | self._task_completed(led) 164 | 165 | def all_off(self): 166 | blinkt.clear() 167 | blinkt.show() 168 | 169 | def test(self): 170 | self.all_off() 171 | n_leds = 8 172 | colors = ['red', 'green', 'blue', 'white'] 173 | for l in range(n_leds): 174 | for c in colors: 175 | if c == 'red': 176 | r = 255 177 | g = 0 178 | b = 0 179 | elif c == 'green': 180 | r = 0 181 | g = 255 182 | b = 0 183 | elif c == 'blue': 184 | r = 0 185 | g = 0 186 | b = 255 187 | elif c == 'white': 188 | r = 255 189 | g = 255 190 | b = 255 191 | print(f'LED: {l}, color: {c}') 192 | blinkt.set_pixel(l, r, g, b, 0.07) 193 | blinkt.show() 194 | time.sleep(.01) 195 | 196 | for _ in range(3): 197 | blinkt.set_all(255,255,255) 198 | blinkt.show() 199 | time.sleep(.05) 200 | blinkt.set_all(0,0,0) 201 | blinkt.show() 202 | time.sleep(.05) 203 | 204 | self.all_off() 205 | 206 | def _begin_task(self,i): 207 | blinkt.set_pixel(i, 0, 0, 255, 0.07) 208 | blinkt.show() 209 | 210 | def _task_completed(self,i): 211 | blinkt.set_pixel(i, 0, 255, 0, 0.07) 212 | blinkt.show() 213 | 214 | def _task_errored(self,i): 215 | blinkt.set_pixel(i, 255, 0, 0, 0.07) 216 | blinkt.show() 217 | 218 | 219 | if __name__ == '__main__': 220 | main() 221 | -------------------------------------------------------------------------------- /home/pi/backup-iphone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "$(date) : Welcome to backup-iphone.sh" 4 | 5 | export SHELL=/bin/bash 6 | export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/home/pi/usr/bin 7 | export LD_LIBRARY_PATH=/home/pi/usr/lib 8 | 9 | echo "Running backup-iphone.py..." 10 | 11 | # -u: stdout & stderr are unbuffered. Else our tail -f isn't real-time. 12 | python3 -u /home/pi/backup-iphone.py >> /home/pi/log.txt 2>&1 13 | 14 | echo "$(date) : goodbye from backup-iphone.sh" 15 | -------------------------------------------------------------------------------- /home/pi/blinkt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : <<'DISCLAIMER' 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 6 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 7 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 8 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 9 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 10 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 11 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 12 | OTHER DEALINGS IN THE SOFTWARE. 13 | 14 | This script is licensed under the terms of the MIT license. 15 | Unless otherwise noted, code reproduced herein 16 | was written for this script. 17 | 18 | - The Pimoroni Crew - 19 | 20 | DISCLAIMER 21 | 22 | # script control variables 23 | 24 | productname="Blinkt!" # the name of the product to install 25 | scriptname="blinkt" # the name of this script 26 | spacereq=50 # minimum size required on root partition in MB 27 | debugmode="no" # whether the script should use debug routines 28 | debuguser="none" # optional test git user to use in debug mode 29 | debugpoint="none" # optional git repo branch or tag to checkout 30 | forcesudo="no" # whether the script requires to be ran with root privileges 31 | promptreboot="no" # whether the script should always prompt user to reboot 32 | mininstall="no" # whether the script enforces minimum install routine 33 | customcmd="no" # whether to execute commands specified before exit 34 | gpioreq="yes" # whether low-level gpio access is required 35 | i2creq="no" # whether the i2c interface is required 36 | i2sreq="no" # whether the i2s interface is required 37 | spireq="no" # whether the spi interface is required 38 | uartreq="no" # whether uart communication is required 39 | armhfonly="yes" # whether the script is allowed to run on other arch 40 | armv6="yes" # whether armv6 processors are supported 41 | armv7="yes" # whether armv7 processors are supported 42 | armv8="yes" # whether armv8 processors are supported 43 | raspbianonly="no" # whether the script is allowed to run on other OSes 44 | osreleases=( "Raspbian" "Kano" "Mate" "PiTop" "RetroPie" ) # list os-releases supported 45 | oswarning=( "Kali" "OSMC" "Volumio" ) # list experimental os-releases 46 | osdeny=( "Darwin" "Debian" "Linaro" "Ubuntu" ) # list os-releases specifically disallowed 47 | debpackage="blinkt" # the name of the package in apt repo 48 | piplibname="blinkt" # the name of the lib in pip repo 49 | pipoverride="no" # whether the script should give priority to pip repo 50 | pip2support="yes" # whether python2 is supported 51 | pip3support="yes" # whether python3 is supported 52 | topdir="Pimoroni" # the name of the top level directory 53 | localdir="blinkt" # the name of the dir for copy of resources 54 | gitreponame="blinkt" # the name of the git project repo 55 | gitusername="pimoroni" # the name of the git user to fetch repo from 56 | gitrepobranch="master" # repo branch to checkout 57 | gitrepotop="root" # the name of the dir to base repo from 58 | gitrepoclone="no" # whether the git repo is to be cloned locally 59 | gitclonedir="source" # the name of the local dir for repo 60 | repoclean="no" # whether any git repo clone found should be cleaned up 61 | repoinstall="no" # whether the library should be installed from repo 62 | libdir="library" # subdirectory of library in repo 63 | copydir=( "documentation" "examples" ) # subdirectories to copy from repo 64 | copyhead="no" # whether to use the latest repo commit or release tag 65 | pkgremove=() # list of conflicting packages to remove 66 | coredeplist=() # list of core dependencies 67 | pythondep=() # list of python dependencies 68 | pipdeplist=() # list of dependencies to source from pypi 69 | examplesdep=( "numpy" "psutil" "requests" "tweepy" ) # list of python modules required by examples 70 | somemoredep=() # list of additional dependencies 71 | xdisplaydep=() # list of dependencies requiring X server 72 | 73 | # template 1712181300 74 | 75 | FORCE=$1 76 | ASK_TO_REBOOT=false 77 | CURRENT_SETTING=false 78 | MIN_INSTALL=false 79 | FAILED_PKG=false 80 | REMOVE_PKG=false 81 | UPDATE_DB=false 82 | 83 | AUTOSTART=~/.config/lxsession/LXDE-pi/autostart 84 | BOOTCMD=/boot/cmdline.txt 85 | CONFIG=/boot/config.txt 86 | DTBODIR=/boot/overlays 87 | APTSRC=/etc/apt/sources.list 88 | INITABCONF=/etc/inittab 89 | BLACKLIST=/etc/modprobe.d/raspi-blacklist.conf 90 | LOADMOD=/etc/modules 91 | 92 | RASPOOL="http://mirrordirector.raspbian.org/raspbian/pool" 93 | RPIPOOL="http://archive.raspberrypi.org/debian/pool" 94 | DEBPOOL="http://ftp.debian.org/debian/pool" 95 | GETPOOL="https://get.pimoroni.com" 96 | 97 | SMBUS2="python-smbus_3.1.1+svn-2_armhf.deb" 98 | SMBUS3="python3-smbus_3.1.1+svn-2_armhf.deb" 99 | SMBUS35="python3-smbus1_1.1+35dbg-1_armhf.deb" 100 | 101 | SPIDEV2="python-spidev_2.0~git20150907_armhf.deb" 102 | SPIDEV3="python3-spidev_2.0~git20150907_armhf.deb" 103 | 104 | RPIGPIO1="raspi-gpio_0.20170105_armhf.deb" 105 | RPIGPIO2="python-rpi.gpio_0.6.3~jessie-1_armhf.deb" 106 | RPIGPIO3="python3-rpi.gpio_0.6.3~jessie-1_armhf.deb" 107 | 108 | export PIP_FORMAT=legacy 109 | 110 | # function define 111 | 112 | confirm() { 113 | if [ "$FORCE" == '-y' ]; then 114 | true 115 | else 116 | read -r -p "$1 [y/N] " response < /dev/tty 117 | if [[ $response =~ ^(yes|y|Y)$ ]]; then 118 | true 119 | else 120 | false 121 | fi 122 | fi 123 | } 124 | 125 | prompt() { 126 | read -r -p "$1 [y/N] " response < /dev/tty 127 | if [[ $response =~ ^(yes|y|Y)$ ]]; then 128 | true 129 | else 130 | false 131 | fi 132 | } 133 | 134 | success() { 135 | echo -e "$(tput setaf 2)$1$(tput sgr0)" 136 | } 137 | 138 | inform() { 139 | echo -e "$(tput setaf 6)$1$(tput sgr0)" 140 | } 141 | 142 | warning() { 143 | echo -e "$(tput setaf 1)$1$(tput sgr0)" 144 | } 145 | 146 | newline() { 147 | echo "" 148 | } 149 | 150 | progress() { 151 | count=0 152 | until [ $count -eq 7 ]; do 153 | echo -n "..." && sleep 1 154 | ((count++)) 155 | done; 156 | if ps -C $1 > /dev/null; then 157 | echo -en "\r\e[K" && progress $1 158 | fi 159 | } 160 | 161 | sudocheck() { 162 | if [ $(id -u) -ne 0 ]; then 163 | echo -e "Install must be run as root. Try 'sudo ./$scriptname'\n" 164 | exit 1 165 | fi 166 | } 167 | 168 | sysclean() { 169 | sudo apt-get clean && sudo apt-get autoclean 170 | sudo apt-get -y autoremove &> /dev/null 171 | } 172 | 173 | sysupdate() { 174 | if ! $UPDATE_DB; then 175 | echo "Updating apt indexes..." && progress apt-get & 176 | sudo apt-get update 1> /dev/null || { warning "Apt failed to update indexes!" && exit 1; } 177 | sleep 3 && UPDATE_DB=true 178 | fi 179 | } 180 | 181 | sysupgrade() { 182 | sudo apt-get upgrade 183 | sudo apt-get clean && sudo apt-get autoclean 184 | sudo apt-get -y autoremove &> /dev/null 185 | } 186 | 187 | sysreboot() { 188 | warning "Some changes made to your system require" 189 | warning "your computer to reboot to take effect." 190 | echo 191 | if prompt "Would you like to reboot now?"; then 192 | sync && sudo reboot 193 | fi 194 | } 195 | 196 | arch_check() { 197 | IS_ARMHF=false 198 | IS_ARMv6=false 199 | 200 | if uname -m | grep -q "armv.l"; then 201 | IS_ARMHF=true 202 | if uname -m | grep -q "armv6l"; then 203 | IS_ARMv6=true 204 | fi 205 | fi 206 | } 207 | 208 | os_check() { 209 | IS_MACOSX=false 210 | IS_RASPBIAN=false 211 | IS_SUPPORTED=false 212 | IS_EXPERIMENTAL=false 213 | OS_NAME="Unknown" 214 | 215 | if uname -s | grep -q "Darwin"; then 216 | OS_NAME="Darwin" && IS_MACOSX=true 217 | elif cat /etc/os-release | grep -q "Kali"; then 218 | OS_NAME="Kali" 219 | elif [ -d ~/.kano-settings ] || [ -d ~/.kanoprofile ]; then 220 | OS_NAME="Kano" 221 | elif whoami | grep -q "linaro"; then 222 | OS_NAME="Linaro" 223 | elif [ -d ~/.config/ubuntu-mate ];then 224 | OS_NAME="Mate" 225 | elif [ -d ~/.pt-os-dashboard ] || [ -d ~/.pt-dashboard ] || [ -f ~/.pt-dashboard-config ]; then 226 | OS_NAME="PiTop" 227 | elif command -v emulationstation > /dev/null; then 228 | OS_NAME="RetroPie" 229 | elif cat /etc/os-release | grep -q "OSMC"; then 230 | OS_NAME="OSMC" 231 | elif cat /etc/os-release | grep -q "volumio"; then 232 | OS_NAME="Volumio" 233 | elif cat /etc/os-release | grep -q "Raspbian"; then 234 | OS_NAME="Raspbian" && IS_RASPBIAN=true 235 | elif cat /etc/os-release | grep -q "Debian"; then 236 | OS_NAME="Debian" 237 | elif cat /etc/os-release | grep -q "Ubuntu"; then 238 | OS_NAME="Ubuntu" 239 | fi 240 | 241 | if [[ " ${osreleases[@]} " =~ " ${OS_NAME} " ]]; then 242 | IS_SUPPORTED=true 243 | fi 244 | if [[ " ${oswarning[@]} " =~ " ${OS_NAME} " ]]; then 245 | IS_EXPERIMENTAL=true 246 | fi 247 | } 248 | 249 | raspbian_check() { 250 | IS_SUPPORTED=false 251 | IS_EXPERIMENTAL=false 252 | 253 | if [ -f /etc/os-release ]; then 254 | if cat /etc/os-release | grep -q "/sid"; then 255 | IS_SUPPORTED=false && IS_EXPERIMENTAL=true 256 | elif cat /etc/os-release | grep -q "stretch"; then 257 | IS_SUPPORTED=true && IS_EXPERIMENTAL=false 258 | elif cat /etc/os-release | grep -q "jessie"; then 259 | IS_SUPPORTED=true && IS_EXPERIMENTAL=false 260 | elif cat /etc/os-release | grep -q "wheezy"; then 261 | IS_SUPPORTED=true && IS_EXPERIMENTAL=false 262 | else 263 | IS_SUPPORTED=false && IS_EXPERIMENTAL=false 264 | fi 265 | fi 266 | } 267 | 268 | home_dir() { 269 | if [ $EUID -ne 0 ]; then 270 | if $IS_MACOSX; then 271 | USER_HOME=$(dscl . -read /Users/$USER NFSHomeDirectory | cut -d: -f2) 272 | else 273 | USER_HOME=$(getent passwd $USER | cut -d: -f6) 274 | fi 275 | else 276 | warning "Running as root, please log in as a regular user with sudo rights!" 277 | echo && exit 1 278 | fi 279 | } 280 | 281 | space_chk() { 282 | if command -v stat > /dev/null && ! $IS_MACOSX; then 283 | if [ $spacereq -gt $(($(stat -f -c "%a*%S" /)/10**6)) ];then 284 | echo 285 | warning "There is not enough space left to proceed with installation" 286 | if confirm "Would you like to attempt to expand your filesystem?"; then 287 | curl -sS $GETPOOL/expandfs | sudo bash && exit 1 288 | else 289 | echo && exit 1 290 | fi 291 | fi 292 | fi 293 | } 294 | 295 | timestamp() { 296 | date +%Y%m%d-%H%M 297 | } 298 | 299 | check_network() { 300 | sudo ping -q -w 10 -c 1 8.8.8.8 | grep "received, 0" &> /dev/null && return 0 || return 1 301 | } 302 | 303 | launch_url() { 304 | check_network || (error_box "You don't appear to be connected to the internet, please check your connection and try again!" && exit 1) 305 | if command -v xdg-open > /dev/null; then 306 | xdg-open "$1" && return 0 307 | else 308 | error_box "There was an error attempting to launch your browser!" 309 | fi 310 | } 311 | 312 | get_install() { 313 | check_network || (error_box "You don't appear to be connected to the internet, please check your connection and try again!" && exit 1) 314 | if [ "$1" != diagnostic ];then 315 | sysupdate && UPDATE_DB=true 316 | fi 317 | if ! command -v curl > /dev/null; then 318 | apt_pkg_install "curl" 319 | fi 320 | curl -sS https://get.pimoroni.com/$1 | bash -s - "-y" $2 321 | read -p "Press Enter to continue..." < /dev/tty 322 | } 323 | 324 | apt_pkg_req() { 325 | APT_CHK=$(dpkg-query -W -f='${Status}\n' "$1" 2> /dev/null | grep "install ok installed") 326 | 327 | if [ "" == "$APT_CHK" ]; then 328 | echo "$1 is required" 329 | true 330 | else 331 | echo "$1 is already installed" 332 | false 333 | fi 334 | } 335 | 336 | apt_pkg_install() { 337 | echo "Installing $1..." 338 | sudo apt-get --yes install "$1" 1> /dev/null || { inform "Apt failed to install $1!\nFalling back on pypi..." && return 1; } 339 | } 340 | 341 | apt_deb_chk() { 342 | BEFORE=$(dpkg-query -W "$1" 2> /dev/null) 343 | sudo apt-get --yes install "$1" &> /dev/null || return 1 344 | AFTER=$(dpkg-query -W "$1" 2> /dev/null) 345 | if [ "$BEFORE" == "$AFTER" ]; then 346 | echo "$1 is already the newest version" 347 | else 348 | echo "$1 was successfully upgraded" 349 | fi 350 | } 351 | 352 | apt_deb_install() { 353 | echo "Installing $1..." 354 | if [[ "$1" != *".deb"* ]]; then 355 | sudo apt-get --yes install "$1" &> /dev/null || inform "Apt failed to install $1!\nFalling back on pypi..." 356 | dpkg-query -W -f='${Status}\n' "$1" 2> /dev/null | grep "install ok installed" 357 | else 358 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` 359 | cd $DEBDIR 360 | wget "$GETPOOL/resources/$1" &> /dev/null 361 | sudo dpkg -i "$DEBDIR/$1" | grep "Installing $1" 362 | fi 363 | } 364 | 365 | pip_cmd_chk() { 366 | if command -v pip2 > /dev/null; then 367 | PIP2_BIN="pip2" 368 | elif command -v pip-2.7 > /dev/null; then 369 | PIP2_BIN="pip-2.7" 370 | elif command -v pip-2.6 > /dev/null; then 371 | PIP2_BIN="pip-2.6" 372 | else 373 | PIP2_BIN="pip" 374 | fi 375 | if command -v pip3 > /dev/null; then 376 | PIP3_BIN="pip3" 377 | elif command -v pip-3.3 > /dev/null; then 378 | PIP3_BIN="pip-3.3" 379 | elif command -v pip-3.2 > /dev/null; then 380 | PIP3_BIN="pip-3.2" 381 | fi 382 | } 383 | 384 | pip2_lib_req() { 385 | PIP2_CHK=$($PIP2_BIN list 2> /dev/null | grep -i "$1") 386 | 387 | if [ -z "$PIP2_CHK" ]; then 388 | true 389 | else 390 | false 391 | fi 392 | } 393 | 394 | pip3_lib_req() { 395 | PIP3_CHK=$($PIP3_BIN list 2> /dev/null | grep -i "$1") 396 | 397 | if [ -z "$PIP3_CHK" ]; then 398 | true 399 | else 400 | false 401 | fi 402 | } 403 | 404 | usb_max_power() { 405 | if grep -q "^max_usb_current=1$" $CONFIG; then 406 | echo -e "\nMax USB current setting already active" 407 | else 408 | echo -e "\nAdjusting USB current setting in $CONFIG" 409 | echo "max_usb_current=1" | sudo tee -a $CONFIG &> /dev/null 410 | fi 411 | } 412 | 413 | add_dtoverlay() { 414 | if grep -q "^dtoverlay=$1" $CONFIG; then 415 | echo -e "\n$1 overlay already active" 416 | elif grep -q "^#dtoverlay=$1" $CONFIG; then 417 | sudo sed -i "/^#dtoverlay=$1$/ s|#||" $CONFIG 418 | echo -e "\nAdding $1 overlay to $CONFIG" 419 | ASK_TO_REBOOT=true 420 | else 421 | echo "dtoverlay=$1" | sudo tee -a $CONFIG &> /dev/null 422 | echo -e "\nAdding $1 overlay to $CONFIG" 423 | ASK_TO_REBOOT=true 424 | fi 425 | } 426 | 427 | remove_dtoverlay() { 428 | sudo sed -i "/^dtoverlay=$1$/ s|^|#|" $CONFIG 429 | ASK_TO_REBOOT=true 430 | } 431 | 432 | enable_pi_audio() { 433 | if grep -q "#dtparam=audio=on" $CONFIG; then 434 | sudo sed -i "/^#dtparam=audio=on$/ s|#||" $CONFIG 435 | echo -e "\nsnd_bcm2835 loaded (on-board audio enabled)" 436 | ASK_TO_REBOOT=true 437 | fi 438 | } 439 | 440 | disable_pi_audio() { 441 | if grep -q "^dtparam=audio=on" $CONFIG; then 442 | sudo sed -i "/^dtparam=audio=on$/ s|^|#|" $CONFIG 443 | echo -e "\nsnd_bcm2835 unloaded (on-board audio disabled)" 444 | ASK_TO_REBOOT=true 445 | fi 446 | } 447 | 448 | test_audio() { 449 | echo 450 | if confirm "Do you wish to test your system now?"; then 451 | echo -e "\nTesting..." 452 | speaker-test -l5 -c2 -t wav 453 | fi 454 | } 455 | 456 | disable_pulseaudio() { 457 | sudo mv /etc/xdg/autostart/pulseaudio.desktop /etc/xdg/autostart/pulseaudio.disabled &> /dev/null 458 | pulseaudio -k &> /dev/null 459 | } 460 | 461 | kill_volumealsa() { 462 | sed -i "s|type=volumealsa|type=space|" $HOME/.config/lxpanel/LXDE/panels/panel &> /dev/null 463 | sed -i "s|type=volumealsa|type=space|" $HOME/.config/lxpanel/LXDE-pi/panels/panel &> /dev/null 464 | } 465 | 466 | basic_asound() { 467 | sudo echo -e "pcm.\041default {\n type hw\n card 1\n}" > $HOME/.asoundrc 468 | sudo echo -e "ctl.\041default {\n type hw\n card 1\n}" >> $HOME/.asoundrc 469 | sudo mv $HOME/.asoundrc /etc/asound.conf 470 | } 471 | 472 | config_set() { 473 | if [ -n $defaultconf ]; then 474 | sudo sed -i "s|$1=.*$|$1=$2|" $defaultconf 475 | else 476 | sudo sed -i "s|$1=.*$|$1=$2|" $3 477 | fi 478 | } 479 | 480 | servd_trig() { 481 | if command -v service > /dev/null; then 482 | sudo service $1 $2 483 | fi 484 | } 485 | 486 | get_init_sys() { 487 | if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then 488 | SYSTEMD=1 489 | elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then 490 | SYSTEMD=0 491 | else 492 | echo "Unrecognised init system" && exit 1 493 | fi 494 | } 495 | 496 | i2c_vc_dtparam() { 497 | if [ -e $CONFIG ] && grep -q "^dtparam=i2c_vc=on$" $CONFIG; then 498 | echo -e "\ni2c0 bus already active" 499 | else 500 | echo -e "\nEnabling i2c0 bus in $CONFIG" 501 | echo "dtparam=i2c_vc=on" | sudo tee -a $CONFIG && echo 502 | fi 503 | } 504 | 505 | : <<'MAINSTART' 506 | 507 | Perform all variables declarations as well as function definition 508 | above this section for clarity, thanks! 509 | 510 | MAINSTART 511 | 512 | # intro message 513 | 514 | if [ $debugmode != "no" ]; then 515 | if [ $debuguser != "none" ]; then 516 | gitusername="$debuguser" 517 | fi 518 | if [ $debugpoint != "none" ]; then 519 | gitrepobranch="$debugpoint" 520 | fi 521 | inform "\nDEBUG MODE ENABLED" 522 | echo -e "git user $gitusername and $gitrepobranch branch/tag will be used\n" 523 | else 524 | echo -e "\nThis script will install everything needed to use\n$productname" 525 | if [ "$FORCE" != '-y' ]; then 526 | inform "\nAlways be careful when running scripts and commands copied" 527 | inform "from the internet. Ensure they are from a trusted source.\n" 528 | echo -e "If you want to see what this script does before running it," 529 | echo -e "you should run: 'curl $GETPOOL/$scriptname'\n" 530 | fi 531 | fi 532 | 533 | # checks and init 534 | 535 | arch_check 536 | os_check 537 | space_chk 538 | home_dir 539 | 540 | if [ $debugmode != "no" ]; then 541 | echo "USER_HOME is $USER_HOME" 542 | echo "OS_NAME is $OS_NAME" 543 | echo "IS_SUPPORTED is $IS_SUPPORTED" 544 | echo "IS_EXPERIMENTAL is $IS_EXPERIMENTAL" 545 | echo 546 | fi 547 | 548 | if ! $IS_ARMHF; then 549 | warning "This hardware is not supported, sorry!" 550 | warning "Config files have been left untouched\n" 551 | exit 1 552 | fi 553 | 554 | if $IS_ARMv8 && [ $armv8 == "no" ]; then 555 | warning "Sorry, your CPU is not supported by this installer\n" 556 | exit 1 557 | elif $IS_ARMv7 && [ $armv7 == "no" ]; then 558 | warning "Sorry, your CPU is not supported by this installer\n" 559 | exit 1 560 | elif $IS_ARMv6 && [ $armv6 == "no" ]; then 561 | warning "Sorry, your CPU is not supported by this installer\n" 562 | exit 1 563 | fi 564 | 565 | if [ $raspbianonly == "yes" ] && ! $IS_RASPBIAN;then 566 | warning "This script is intended for Raspbian on a Raspberry Pi!\n" 567 | exit 1 568 | fi 569 | 570 | if $IS_RASPBIAN; then 571 | raspbian_check 572 | if ! $IS_SUPPORTED && ! $IS_EXPERIMENTAL; then 573 | warning "\n--- Warning ---\n" 574 | echo "The $productname installer" 575 | echo "does not work on this version of Raspbian." 576 | echo "Check https://github.com/$gitusername/$gitreponame" 577 | echo "for additional information and support" && echo 578 | exit 1 579 | fi 580 | fi 581 | 582 | if ! $IS_SUPPORTED && ! $IS_EXPERIMENTAL; then 583 | warning "Your operating system is not supported, sorry!\n" 584 | exit 1 585 | fi 586 | 587 | if $IS_EXPERIMENTAL; then 588 | warning "\nSupport for your operating system is experimental. Please visit" 589 | warning "forums.pimoroni.com if you experience issues with this product.\n" 590 | fi 591 | 592 | if [ $forcesudo == "yes" ]; then 593 | sudocheck 594 | fi 595 | 596 | if [ $uartreq == "yes" ]; then 597 | echo "Note: $productname requires UART communication" 598 | warning "The serial console will be disabled if you proceed!" 599 | fi 600 | if [ $spireq == "yes" ]; then 601 | echo -e "Note: $productname requires SPI communication" 602 | fi 603 | if [ $i2creq == "yes" ]; then 604 | echo -e "Note: $productname requires I2C communication" 605 | fi 606 | if [ $i2sreq == "yes" ]; then 607 | echo -e "Note: $productname uses the I2S interface" 608 | if [ $OS_NAME != "Volumio" ]; then 609 | warning "The on-board audio chip will be disabled if you proceed!" 610 | fi 611 | fi 612 | 613 | newline 614 | if confirm "Do you wish to continue?"; then 615 | 616 | # basic environment preparation 617 | 618 | echo -e "\nChecking environment..." 619 | 620 | if [ "$FORCE" != '-y' ]; then 621 | if ! check_network; then 622 | warning "We can't connect to the Internet, check your network!" && exit 1 623 | fi 624 | sysupdate && newline 625 | fi 626 | 627 | if apt_pkg_req "apt-utils" &> /dev/null; then 628 | apt_pkg_install "apt-utils" 629 | fi 630 | if ! command -v curl > /dev/null; then 631 | apt_pkg_install "curl" 632 | fi 633 | if ! command -v wget > /dev/null; then 634 | apt_pkg_install "wget" 635 | fi 636 | 637 | if [ "$pip2support" == "yes" ]; then 638 | if ! [ -f "$(which python2)" ]; then 639 | if confirm "Python 2 is not installed. Would like to install it?"; then 640 | progress apt-get & 641 | apt_pkg_install "python-pip" 642 | else 643 | pip2support="na" 644 | fi 645 | elif apt_pkg_req "python-pip" &> /dev/null; then 646 | progress apt-get & 647 | apt_pkg_install "python-pip" 648 | fi 649 | fi 650 | if [ "$pip3support" == "yes" ]; then 651 | if ! [ -f "$(which python3)" ]; then 652 | if prompt "Python 3 is not installed. Would like to install it?"; then 653 | progress apt-get & 654 | apt_pkg_install "python3-pip" 655 | else 656 | pip3support="na" 657 | fi 658 | elif apt_pkg_req "python3-pip" &> /dev/null; then 659 | progress apt-get & 660 | apt_pkg_install "python3-pip" 661 | fi 662 | fi 663 | pip_cmd_chk 664 | 665 | # hardware setup 666 | 667 | echo -e "\nChecking hardware requirements..." 668 | 669 | if [ $uartreq == "yes" ]; then 670 | echo -e "\nThe serial console must be disabled for $productname to work" 671 | curl -sS $GETPOOL/uarton | sudo bash -s - "-y" && ASK_TO_REBOOT=true 672 | fi 673 | 674 | if [ $gpioreq == "yes" ]; then 675 | echo -e "\nChecking for packages required for GPIO control..." 676 | if ! apt_pkg_install "raspi-gpio" &> /dev/null; then 677 | echo "package raspi-gpio can't be found, fetching from alternative location..." 678 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR 679 | wget $RPIPOOL/main/r/raspi-gpio/$RPIGPIO1 &> /dev/null 680 | sudo dpkg -i $DEBDIR/$RPIGPIO1 && FAILED_PKG=false 681 | fi 682 | if [ "$pip2support" == "yes" ] && ! apt_pkg_install "python-rpi.gpio" &> /dev/null; then 683 | if [ -n $(python --version 2>&1 | grep -q "2.7") ]; then 684 | echo "package python-rpi.gpio can't be found, fetching from alternative location..." 685 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR 686 | wget $RPIPOOL/main/r/rpi.gpio/$RPIGPIO2 &> /dev/null 687 | sudo dpkg -i $DEBDIR/$RPIGPIO2 && FAILED_PKG=false 688 | else 689 | sudo $PIP2_BIN install RPi.GPIO && FAILED_PKG=false 690 | fi 691 | fi 692 | if [ "$pip3support" == "yes" ] && ! apt_pkg_install "python3-rpi.gpio" &> /dev/null; then 693 | if [ -n $(python3 --version 2>&1 | grep -q "3.4") ]; then 694 | echo "package python3-rpi.gpio can't be found, fetching from alternative location..." 695 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR 696 | wget $RPIPOOL/main/r/rpi.gpio/$RPIGPIO3 &> /dev/null 697 | sudo dpkg -i $DEBDIR/$RPIGPIO3 && FAILED_PKG=false 698 | else 699 | sudo $PIP3_BIN install RPi.GPIO && FAILED_PKG=false 700 | fi 701 | fi 702 | if [ "$pip2support" == "yes" ] && [ -f "$(which python2)" ]; then 703 | if ! $PIP2_BIN list | grep "RPi.GPIO" &> /dev/null; then 704 | warning "Unable to install RPi.GPIO for python 2!" && FAILED_PKG=true 705 | else 706 | RPIGPIO2="install ok installed" 707 | fi 708 | fi 709 | if [ "$pip3support" == "yes" ] && [ -f "$(which python3)" ]; then 710 | if ! $PIP3_BIN list | grep "RPi.GPIO" &> /dev/null; then 711 | warning "Unable to install RPi.GPIO for python 3!" && FAILED_PKG=true 712 | else 713 | RPIGPIO3="install ok installed" 714 | fi 715 | fi 716 | if [ "$RPIGPIO2" == "install ok installed" ] || [ "$RPIGPIO3" == "install ok installed" ]; then 717 | if ! $FAILED_PKG; then 718 | echo -e "RPi.GPIO installed and up-to-date" 719 | fi 720 | fi 721 | fi 722 | 723 | if [ $spireq == "yes" ]; then 724 | newline 725 | if ls /dev/spi* &> /dev/null; then 726 | inform "SPI already enabled" 727 | else 728 | echo "SPI must be enabled for $productname to work" 729 | if command -v raspi-config > /dev/null && sudo raspi-config nonint get_spi | grep -q "1"; then 730 | sudo raspi-config nonint do_spi 0 731 | inform "SPI is now enabled" 732 | else 733 | curl -sS $GETPOOL/spi | sudo bash -s - "-y" && ASK_TO_REBOOT=true 734 | fi 735 | fi 736 | echo -e "\nChecking packages required by SPI interface..." 737 | if [ "$pip2support" == "yes" ] && ! apt_pkg_install "python-spidev" &> /dev/null; then 738 | if [ -n $(python --version 2>&1 | grep -q "2.7") ]; then 739 | echo "package python-spidev can't be found, fetching from alternative location..." 740 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR 741 | wget $RPIPOOL/main/s/spidev/$SPIDEV2 &> /dev/null 742 | sudo dpkg -i $DEBDIR/$SPIDEV2 && FAILED_PKG=false 743 | else 744 | sudo $PIP2_BIN install spidev && FAILED_PKG=false 745 | fi 746 | fi 747 | if [ "$pip3support" == "yes" ] && ! apt_pkg_install "python3-spidev" &> /dev/null; then 748 | if [ -n $(python3 --version 2>&1 | grep -q "3.4") ]; then 749 | echo "package python3-spidev can't be found, fetching from alternative location..." 750 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR 751 | wget $RPIPOOL/main/s/spidev/$SPIDEV3 &> /dev/null 752 | sudo dpkg -i $DEBDIR/$SPIDEV3 && FAILED_PKG=false 753 | else 754 | sudo $PIP3_BIN install spidev && FAILED_PKG=false 755 | fi 756 | fi 757 | if [ "$pip2support" == "yes" ] && [ -f "$(which python2)" ]; then 758 | if ! $PIP2_BIN list | grep "spidev" &> /dev/null; then 759 | warning "Unable to install spidev for python 2!" && FAILED_PKG=true 760 | else 761 | SPIDEV2="install ok installed" 762 | fi 763 | fi 764 | if [ "$pip3support" == "yes" ] && [ -f "$(which python3)" ]; then 765 | if ! $PIP3_BIN list | grep "spidev" &> /dev/null; then 766 | warning "Unable to install spidev for python 3!" && FAILED_PKG=true 767 | else 768 | SPIDEV3="install ok installed" 769 | fi 770 | fi 771 | if [ "$SPIDEV2" == "install ok installed" ] || [ "$SPIDEV3" == "install ok installed" ]; then 772 | if ! $FAILED_PKG; then 773 | echo -e "spidev installed and up-to-date" 774 | fi 775 | fi 776 | fi 777 | 778 | if [ $i2creq == "yes" ]; then 779 | newline 780 | if ls /dev/i2c* &> /dev/null; then 781 | inform "I2C already enabled" 782 | else 783 | echo "I2C must be enabled for $productname to work" 784 | if command -v raspi-config > /dev/null && sudo raspi-config nonint get_i2c | grep -q "1"; then 785 | sudo raspi-config nonint do_i2c 0 786 | inform "I2C is now enabled" 787 | else 788 | curl -sS $GETPOOL/i2c | sudo bash -s - "-y" && ASK_TO_REBOOT=true 789 | fi 790 | fi 791 | echo -e "\nChecking packages required by I2C interface..." 792 | if [ "$pip2support" == "yes" ] && ! apt_pkg_install "python-smbus" &> /dev/null; then 793 | if [ -n $(python --version 2>&1 | grep -q "2.7") ]; then 794 | echo "package python-smbus can't be found, fetching from alternative location..." 795 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR 796 | wget $RPIPOOL/main/i/i2c-tools/$SMBUS2 &> /dev/null 797 | sudo dpkg -i $DEBDIR/$SMBUS2 && FAILED_PKG=false 798 | fi 799 | fi 800 | if [ "$pip3support" == "yes" ] && ! apt_pkg_install "python3-smbus" &> /dev/null; then 801 | if [ -n $(python3 --version 2>&1 | grep -q "3.4") ]; then 802 | echo "package python3-smbus can't be found, fetching from alternative location..." 803 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR 804 | wget $RPIPOOL/main/i/i2c-tools/$SMBUS3 &> /dev/null 805 | sudo dpkg -i $DEBDIR/$SMBUS3 && FAILED_PKG=false 806 | elif [ -n $(python3 --version 2>&1 | grep -q "3.5") ]; then 807 | if apt_pkg_req "python3-smbus1" &> /dev/null; then 808 | echo "package python3-smbus can't be found, fetching from alternative location..." 809 | DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR 810 | wget $GETPOOL/resources/$SMBUS35 &> /dev/null 811 | sudo dpkg -i $DEBDIR/$SMBUS35 && FAILED_PKG=false 812 | fi 813 | fi 814 | fi 815 | if [ "$pip2support" == "yes" ] && [ -f "$(which python2)" ]; then 816 | if ! $PIP2_BIN list | grep "smbus" &> /dev/null; then 817 | warning "Unable to install smbus for python 2!" && FAILED_PKG=true 818 | else 819 | SMBUS2="install ok installed" 820 | fi 821 | fi 822 | if [ "$pip3support" == "yes" ] && [ -f "$(which python3)" ]; then 823 | if ! $PIP3_BIN list | grep "smbus" &> /dev/null; then 824 | warning "Unable to install smbus for python 3!" && FAILED_PKG=true 825 | else 826 | SMBUS3="install ok installed" 827 | fi 828 | fi 829 | if [ "$SMBUS2" == "install ok installed" ] || [ "$SMBUS3" == "install ok installed" ]; then 830 | if ! $FAILED_PKG; then 831 | echo -e "smbus installed and up-to-date" 832 | fi 833 | fi 834 | fi 835 | 836 | if [ $i2sreq == "yes" ]; then 837 | if [ -f /etc/asound.conf ]; then 838 | sudo rm -f /etc/asound.conf.backup &> /dev/null 839 | sudo mv /etc/asound.conf /etc/asound.conf.backup 840 | inform "existing config backed up to /etc/asound.conf.backup" 841 | fi 842 | if [ -f $HOME/.asoundrc ]; then 843 | sudo rm -f $HOME/.asoundrc.backup &> /dev/null 844 | sudo mv $HOME/.asoundrc $HOME/.asoundrc.backup 845 | inform "existing config backed up to ~/.asound.conf.backup" 846 | fi 847 | fi 848 | 849 | # minimum install routine 850 | 851 | if [ $mininstall != "yes" ] && [ $gitrepoclone != "yes" ]; then 852 | newline 853 | echo "$productname comes with examples and documentation that you may wish to install." 854 | echo "Performing a full install will ensure those resources are installed," 855 | echo "along with all required dependencies. It may however take a while!" 856 | newline 857 | if ! confirm "Do you wish to perform a full install?"; then 858 | MIN_INSTALL=true 859 | fi 860 | else 861 | MIN_INSTALL=true 862 | fi 863 | 864 | if [ $localdir != "na" ]; then 865 | installdir="$USER_HOME/$topdir/$localdir" 866 | else 867 | installdir="$USER_HOME/$topdir" 868 | fi 869 | 870 | if ! $MIN_INSTALL || [ $gitrepoclone == "yes" ]; then 871 | [ -d $installdir ] || mkdir -p $installdir 872 | fi 873 | 874 | if [ $debugmode != "no" ]; then 875 | echo "INSTALLDIR is $installdir" 876 | fi 877 | 878 | # apt repo install 879 | 880 | echo -e "\nChecking for dependencies..." 881 | 882 | if $REMOVE_PKG; then 883 | for pkgrm in ${pkgremove[@]}; do 884 | warning "Installed package conflicts with requirements" 885 | sudo apt-get remove "$pkgrm" 886 | done 887 | fi 888 | 889 | for pkgdep in ${coredeplist[@]}; do 890 | if apt_pkg_req "$pkgdep"; then 891 | apt_pkg_install "$pkgdep" 892 | fi 893 | done 894 | 895 | for pkgdep in ${pythondep[@]}; do 896 | if [ -f "$(which python2)" ] && [ $pip2support == "yes" ]; then 897 | if apt_pkg_req "python-$pkgdep"; then 898 | apt_pkg_install "python-$pkgdep" 899 | fi 900 | fi 901 | if [ -f "$(which python3)" ] && [ $pip3support == "yes" ]; then 902 | if apt_pkg_req "python3-$pkgdep"; then 903 | apt_pkg_install "python3-$pkgdep" 904 | fi 905 | fi 906 | done 907 | 908 | if [ $pipoverride != "yes" ] && [ $debpackage != "na" ]; then 909 | newline 910 | if [ $pip2support != "yes" ] && [ $pip3support != "yes" ]; then 911 | apt_deb_install "$debpackage" 912 | fi 913 | if [ -f "$(which python2)" ] && [ $pip2support == "yes" ]; then 914 | apt_deb_install "python-$debpackage" 915 | if ! apt_pkg_req "python-$debpackage" &> /dev/null; then 916 | sudo $PIP2_BIN uninstall -y "$piplibname" &> /dev/null 917 | fi 918 | fi 919 | if [ -f "$(which python3)" ] && [ $pip3support == "yes" ]; then 920 | apt_deb_install "python3-$debpackage" 921 | if ! apt_pkg_req "python3-$debpackage" &> /dev/null; then 922 | sudo $PIP3_BIN uninstall -y "$piplibname" &> /dev/null 923 | fi 924 | fi 925 | if apt_pkg_req "python-$debpackage" &> /dev/null || apt_pkg_req "python3-$debpackage" &> /dev/null; then 926 | debpackage="na" 927 | fi 928 | else 929 | debpackage="na" 930 | fi 931 | 932 | # pypi repo install 933 | 934 | if [ -f "$(which python2)" ] && [ $pip2support == "yes" ] && apt_pkg_req "python-$debpackage" &> /dev/null; then 935 | if [ $piplibname != "na" ] && [ $debpackage == "na" ]; then 936 | newline && echo "Installing $productname library for Python 2..." && newline 937 | if ! sudo -H $PIP2_BIN install "$piplibname"; then 938 | warning "Python 2 library install failed!" 939 | echo "If problems persist, visit forums.pimoroni.com for support" 940 | exit 1 941 | fi 942 | fi 943 | fi 944 | 945 | if [ -f "$(which python3)" ] && [ $pip3support == "yes" ] && apt_pkg_req "python3-$debpackage" &> /dev/null; then 946 | if [ $piplibname != "na" ] && [ $debpackage == "na" ]; then 947 | newline && echo "Installing $productname library for Python 3..." && newline 948 | if ! sudo -H $PIP3_BIN install "$piplibname"; then 949 | warning "Python 3 library install failed!" 950 | echo "If problems persist, visit forums.pimoroni.com for support" 951 | exit 1 952 | fi 953 | fi 954 | fi 955 | 956 | # git repo install 957 | 958 | if [ $gitrepoclone == "yes" ]; then 959 | if ! command -v git > /dev/null; then 960 | apt_pkg_install git 961 | fi 962 | if [ $gitclonedir == "source" ]; then 963 | gitclonedir=$gitreponame 964 | fi 965 | if [ $repoclean == "yes" ]; then 966 | rm -Rf $installdir/$gitclonedir 967 | fi 968 | if [ -d $installdir/$gitclonedir ]; then 969 | newline && echo "Github repo already present. Updating..." 970 | cd $installdir/$gitclonedir && git pull 971 | else 972 | newline && echo "Cloning Github repo locally..." 973 | cd $installdir 974 | if [ $debugmode != "no" ]; then 975 | echo "git user name is $gitusername" 976 | echo "git repo name is $gitreponame" 977 | fi 978 | if [ $gitrepobranch != "master" ]; then 979 | git clone https://github.com/$gitusername/$gitreponame $gitclonedir -b $gitrepobranch 980 | else 981 | git clone --depth=1 https://github.com/$gitusername/$gitreponame $gitclonedir 982 | fi 983 | fi 984 | fi 985 | 986 | if [ $repoinstall == "yes" ]; then 987 | newline && echo "Installing library..." && newline 988 | cd $installdir/$gitreponame/$libdir 989 | if [ -f "$(which python2)" ] && [ $pip2support == "yes" ]; then 990 | sudo python2 ./setup.py install 991 | fi 992 | if [ -f "$(which python3)" ] && [ $pip3support == "yes" ]; then 993 | sudo python3 ./setup.py install 994 | fi 995 | newline 996 | fi 997 | 998 | # additional install 999 | 1000 | if ! $MIN_INSTALL; then 1001 | echo -e "\nChecking for additional software..." 1002 | for moredep in ${examplesdep[@]}; do 1003 | if [ -f "$(which python2)" ] && apt_pkg_req "python-$moredep"; then 1004 | if ! apt_pkg_install "python-$moredep"; then 1005 | sudo -H $PIP2_BIN install "$moredep" 1006 | if pip2_lib_req "$moredep"; then 1007 | FAILED_PKG=true 1008 | fi 1009 | fi 1010 | fi 1011 | if [ -f "$(which python3)" ] && apt_pkg_req "python3-$moredep"; then 1012 | if ! apt_pkg_install "python3-$moredep"; then 1013 | sudo -H $PIP3_BIN install "$moredep" 1014 | if pip3_lib_req "$moredep"; then 1015 | FAILED_PKG=true 1016 | fi 1017 | fi 1018 | fi 1019 | done 1020 | for pipdep in ${pipdeplist[@]}; do 1021 | if [ -f "$(which python2)" ] && pip2_lib_req "$pipdep"; then 1022 | sudo -H $PIP2_BIN install "$pipdep" 1023 | fi 1024 | if [ -f "$(which python3)" ] && pip3_lib_req "$pipdep"; then 1025 | sudo -H $PIP3_BIN install "$pipdep" 1026 | fi 1027 | done 1028 | for moredep in ${somemoredep[@]}; do 1029 | if apt_pkg_req "$moredep"; then 1030 | apt_pkg_install "$moredep" 1031 | fi 1032 | done 1033 | if [ -n "$DISPLAY" ]; then 1034 | for x11dep in ${xdisplaydep[@]}; do 1035 | if apt_pkg_req "$x11dep"; then 1036 | apt_pkg_install "$x11dep" 1037 | fi 1038 | done 1039 | fi 1040 | fi 1041 | 1042 | # resources install 1043 | 1044 | if ! $MIN_INSTALL && [ -n "$copydir" ]; then 1045 | if ! command -v git > /dev/null; then 1046 | apt_pkg_install git 1047 | fi 1048 | echo -e "\nDownloading examples and documentation..." 1049 | TMPDIR=`mktemp -d /tmp/pimoroni.XXXXXX` 1050 | cd $TMPDIR 1051 | if [ $copyhead != "yes" ]; then 1052 | GITTAG=$(git ls-remote -t https://github.com/$gitusername/$gitreponame v\?.?.? | tail -n 1 | rev | cut -c -6 | rev) 1053 | fi 1054 | if [ -n "$GITTAG" ]; then 1055 | git clone https://github.com/$gitusername/$gitreponame -b $GITTAG &> /dev/null 1056 | else 1057 | git clone --depth=1 https://github.com/$gitusername/$gitreponame &> /dev/null 1058 | fi 1059 | cd $installdir 1060 | for repodir in ${copydir[@]}; do 1061 | if [ -d $repodir ] && [ $repodir != "documentation" ]; then 1062 | newline 1063 | if [ -d $installdir/$repodir-backup ]; then 1064 | rm -R $installdir/$repodir-old &> /dev/null 1065 | mv $installdir/$repodir-backup $installdir/$repodir-old &> /dev/null 1066 | fi 1067 | mv $installdir/$repodir $installdir/$repodir-backup &> /dev/null 1068 | if [ $gitrepotop != "root" ]; then 1069 | cp -R $TMPDIR/$gitreponame/$gitrepotop/$repodir $installdir/$repodir &> /dev/null 1070 | else 1071 | cp -R $TMPDIR/$gitreponame/$repodir $installdir/$repodir &> /dev/null 1072 | fi 1073 | inform "The $repodir directory already exists on your system!" 1074 | echo -e "We've backed them up as $repodir-backup, just in case you've changed anything!\n" 1075 | else 1076 | rm -R $installdir/$repodir &> /dev/null 1077 | if [ $gitrepotop != "root" ]; then 1078 | cp -R $TMPDIR/$gitreponame/$gitrepotop/$repodir $installdir/$repodir &> /dev/null 1079 | else 1080 | cp -R $TMPDIR/$gitreponame/$repodir $installdir/$repodir &> /dev/null 1081 | fi 1082 | fi 1083 | done 1084 | echo "Resources for your $productname were copied to" 1085 | inform "$installdir" 1086 | rm -rf $TMPDIR 1087 | fi 1088 | 1089 | # script custom routines 1090 | 1091 | if [ $customcmd == "no" ]; then 1092 | if [ -n "$pkgremove" ]; then 1093 | echo -e "\nFinalising Install...\n" 1094 | sysclean && newline 1095 | fi 1096 | echo -e "\nAll done. Enjoy your $productname!\n" 1097 | else # custom block starts here 1098 | echo -e "\nFinalising Install...\n" 1099 | # place all custom commands in this scope 1100 | fi 1101 | 1102 | if $FAILED_PKG; then 1103 | warning "\nSome packages could not be installed, review the output for details!\n" 1104 | fi 1105 | if $IS_EXPERIMENTAL; then 1106 | warning "\nSupport for your operating system is experimental. Please visit" 1107 | warning "forums.pimoroni.com if you experience issues with this product.\n" 1108 | fi 1109 | 1110 | if [ "$FORCE" != '-y' ]; then 1111 | if [ $promptreboot == "yes" ] || $ASK_TO_REBOOT; then 1112 | sysreboot && newline 1113 | fi 1114 | fi 1115 | else 1116 | echo -e "\nAborting...\n" 1117 | fi 1118 | 1119 | exit 0 1120 | -------------------------------------------------------------------------------- /home/pi/iphone-backups/iphone files are rsyncd here.txt: -------------------------------------------------------------------------------- 1 | iphone backups will appear here 2 | -------------------------------------------------------------------------------- /home/pi/log.txt: -------------------------------------------------------------------------------- 1 | here's where udev-runs-this.sh and backup-iphone.sh will dump their logs. 2 | 3 | lol. 4 | 5 | -------------------------------------------------------------------------------- /home/pi/monitor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # osascript -e 'tell application "Terminal"' -e 'set the bounds of the front window to {24, 96, 524, 396}' -e 'end tell' 4 | # 1. horiz dist from left side of screen to upper-left corner of window 5 | # 2. vert dist from top of screen to upper-left corner of window 6 | # 3. horiz dist from LHS of screen to lower-right corner of window 7 | # 4. vert dist from RHS of screen to lower-right corner of window 8 | 9 | 10 | osascript -e 'tell app "Terminal" to do script "ssh -t rpi42 watch -d -t lsusb"' 11 | osascript -e 'tell application "Terminal"' -e 'set the bounds of the front window to {1, 1, 500, 500}' -e 'end tell' 12 | osascript -e 'tell app "Terminal" to do script "ssh -t rpi42 dmesg -w"' 13 | osascript -e 'tell application "Terminal"' -e 'set the bounds of the front window to {500, 1, 1000, 500}' -e 'end tell' 14 | osascript -e 'tell app "Terminal" to do script "ssh -t rpi42 tail -f /var/log/syslog"' 15 | osascript -e 'tell application "Terminal"' -e 'set the bounds of the front window to {1000, 1, 1500, 500}' -e 'end tell' 16 | osascript -e 'tell app "Terminal" to do script "ssh -t rpi42 journalctl -f"' 17 | osascript -e 'tell application "Terminal"' -e 'set the bounds of the front window to {1, 550, 500, 1000}' -e 'end tell' 18 | osascript -e 'tell app "Terminal" to do script "ssh -t rpi42 udevadm monitor -kup"' 19 | osascript -e 'tell application "Terminal"' -e 'set the bounds of the front window to {500, 550, 1000, 1000}' -e 'end tell' 20 | osascript -e 'tell app "Terminal" to do script "ssh -t rpi42 watch -d -t /home/pi/usr/bin/idevicepair list "' 21 | osascript -e 'tell application "Terminal"' -e 'set the bounds of the front window to {1000, 550, 1500, 750}' -e 'end tell' 22 | osascript -e 'tell app "Terminal" to do script "ssh -t rpi42 tail -f /home/pi/log.txt "' 23 | osascript -e 'tell application "Terminal"' -e 'set the bounds of the front window to {1000, 750, 1500, 1000}' -e 'end tell' -------------------------------------------------------------------------------- /home/pi/udev-runs-this.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sleep 3 4 | 5 | echo "$(date +%Y-%m-%d-%H-%M-%S) /home/pi/udev-runs-this.sh : about to start backup-iphone.sh | at now." >> "/home/pi/log.txt" 2>&1 6 | 7 | echo "/bin/su -c '/home/pi/backup-iphone.sh >> /home/pi/log.txt 2>&1' pi" | at now 8 | 9 | 10 | echo "$(date +%Y-%m-%d-%H-%M-%S) /home/pi/udev-runs-this.sh : exiting udev-runs-this." >> "/home/pi/log.txt" 2>&1 11 | -------------------------------------------------------------------------------- /home/pi/usr/src/this is where you should git clone libimobiledevice and friends.txt: -------------------------------------------------------------------------------- 1 | this is where you should git clone libimobiledevice and friends 2 | -------------------------------------------------------------------------------- /home/pi/usr/the script backup-iphone.sh mounts the iphone in here.txt: -------------------------------------------------------------------------------- 1 | the script backup-iphone.sh mounts the iphone here 2 | --------------------------------------------------------------------------------