# piflash - flash a Raspberry Pi image to an SD card, with safety checks to avoid erasing wrong device
# by Ian Kluft
use strict;
use warnings;
use v5.14.0; # require 2011 or newer version of Perl
use autodie; # report errors instead of silently continuing ("die" actions are used as exceptions - caught & reported)
use PiFlash;

# PODNAME: piflash
# ABSTRACT: Raspberry Pi SD-flashing script with safety checks to avoid erasing the wrong device

# run main routine
exit PiFlash::main;



=encoding UTF-8

=head1 NAME

piflash - Raspberry Pi SD-flashing script with safety checks to avoid erasing the wrong device

=head1 VERSION

version 0.4.3


 piflash [--verbose] [--resize] [--config conf-file] input-file output-device

 piflash [--verbose] [--config conf-file] --sdsearch

 piflash --version


The "piflash" program writes (or "flashes") an SD card for a Raspberry Pi single-board computer. It includes safety checks so that it can only erase and write to an SD card, not another device on the system. The safety checks are probably of most use to beginners. For more advanced users (like the author, which is why this was written) it also has the convenience of flashing directly from the file formats downloadable from raspberrypi.org without extracting a .img file from a zip/gz/xz file.

=over 1

=item *
The optional parameter --verbose makes much more verbose status and error messages.  Use this when troubleshooting any problem or preparing program output to ask for help or report a bug.

=item *
The optional parameter --resize may be used when writing to an SD card. After writing, it attempts to find the root filesystem on the SD card and resizes it to take the remainder of the free space on the device. This has been tested to work with the popular OS distributions and how they set up their partitions on installation. (However, in case any distributions make changes, please report any errors so they can be fixed.)

=item *
input-file is the path of the binary image file used as input for flashing the SD card. If it's a .img file then it will be flashed directly. If it's a gzip (.gz), xz (.xz) or zip (.zip) file then the .img file will be extracted from it to flash the SD card. It is not necessary to unpack the file if it's in one of these formats. This covers most of the images downloadable from the Raspberry Pi foundation's web site.

=item *
output-file is the path to the block device where the SSD card is located. The device should not be mounted - if it ismounted the script will detect it and exit with an error. This operation will erase the SD card and write the new image from the input-file to it. (So make sure it's an SD card you're willing to have erased.)

=item *
The --sdsearch parameter tells piflash to print a list of device names for SD cards available on the system and then exit. Do not specify an input file or output device when using this option - it will exit before they would be used.

=item *
The --version parameter tells piflash to print its version number and exit.


=head2 Safety Checks

The program makes a number of safety checks for you. Since the SD card flashing process may need root permissions, these are considered prudent precautions.

=over 1

=item *
The input file's format will be checked. If it ends in .img then it will be flashed directly as a binary image file. If it ends in .xz, .gzip or .zip, it will extract the binary image from the file. If the filename doesn't have a suffix, libmagic will be used to inspect the contents of the file (for "magic numbers") to determine its format.

=item *
The output device must be a block device.

=item *
If the output device is a mounted filesystem, it will refuse to erase it.

=item *
If the output device is not an SD card, it will refuse to erase it.
Piflash has been tested with USB and PCI based SD card interfaces.


=head2 Automated Flashing Procedure

Piflash automates the process of flashing an SD card from various Raspberry Pi OS images.

=over 1

=item *
For most disk images, either in a raw *.img file, compressed in a *.gz or *.xz file, or included in a *.zip archive, piflash recognizes the file format and extracts the disk image for flashing, eliminating the step of uncompressing or unarchiving it before it can be flashed to the SD.

=item *
For zip archives, it checks if it contains the Raspberry Pi NOOBS (New Out Of the Box System), in which case it handles it differently. The steps it takes are similar to the instructions that one would have to follow manually.  It formats a new VFAT filesystem on the card. (FAT/VFAT is the only format recognized by the Raspberry Pi's simple boot loader.) Then it copies the contents of the zip archive into the card, automating the entire flashing process even for a NOOBS system, which previously didn't even have instructions to be done from Linux systems.

=item *
When the --resize option is provided, it requests piflash to resize the root filesystem to the maximum available size of the SD card. It's ignored for NOOBS because it will wipe out the initial partitions upong installing anything else. In all other cases, it will expand the last filesystem on the SD card, which is traditionally where the root filesystem is placed. (Exceptions may be added if needed for distributions which don't follow this layout.)



The piflash script only works on Linux systems. It depends on features of the Linux kernel to look up whether the output device is an SD card and other information about it. It has been tested so far on Fedora and Ubuntu to get the kernel parameters right for various USB SD card adapters.

=head2 System Dependencies

Some programs and libraries must be installed on the system for piflash to work - most packages have such dependencies.

On RPM-based Linux systems (Red Hat, Fedora, CentOS) the following command, run as root, will install the dependencies.

	dnf install coreutils util-linux sudo perl file-libs perl-File-LibMagic perl-IO perl-Exception-Class perl-Try-Tiny perl-Module-Pluggable perl-File-Path perl-YAML-LibYAML gzip unzip xz e2fsprogs dosfstools

On Deb-based Linux systems (Debian, Ubuntu, Raspbian) the following command, run as root, will install the dependencies.

	apt-get install coreutils util-linux klibc-utils sudo perl-base libmagic1 libfile-libmagic-perl libio-all-perl libexception-class-perl libtry-tiny-perl libmodule-pluggable-perl  libyaml-libyaml-perl gzip xz-utils e2fsprogs dosfstools

On source-based or other Linux distributions, make sure the following are installed:

=over 1

=item programs:
blockdev, dd, echo, gunzip, lsblk, mkdir, mkfs.vfat, mount, perl, sfdisk, sudo, sync, true, umount, unzip, xz

=item libraries:
libmagic/file-libs, File::LibMagic (perl), IO (perl), Exception::Class (perl), Module::Pluggable (perl), YAML::LibYAML (perl), File::Path (perl)


=head2 Piflash script

This is the Perl distribution PiFlash.

Installing PiFlash is straightforward.

=head3 Installation with cpanm

If you have cpanm, you only need one line:

    % cpanm PiFlash

If it does not have permission to install modules to the current perl, cpanm
will automatically set up and install to a local::lib in your home directory.
See the local::lib documentation (L<https://metacpan.org/pod/local::lib>) for
details on enabling it in your environment.

=head3 Installing with the CPAN shell

Alternatively, if your CPAN shell is set up, you should just be able to do:

    % cpan PiFlash

=head3 Manual installation

As a last resort, you can manually install it. Download the tarball, untar it,
install configure prerequisites (see below), then build it:

    % perl Makefile.PL
    % make && make test

Then install it:

    % make install

If your perl is system-managed, you can create a local::lib in your home
directory to install modules to. For details, see the local::lib documentation:

The prerequisites of this distribution will also have to be installed manually. The
prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated
by running the manual build process described above.

=head3 Configure prerequisites

This distribution requires other modules to be installed before this
distribution's installer can be run.  They can be found under the
"configure_requires" key of META.yml or the
"{prereqs}{configure}{requires}" key of META.json.

=head3 Documentation

PiFlash documentation is available as POD.
You can run `perldoc` from a shell to read the documentation:

    % perldoc piflash

For more information on installing Perl modules via CPAN, please see:

=head3 Online resources

A list of online resources for PiFlash is at L<https://metacpan.org/pod/distribution/PiFlash/doc/resources>
including where to download Raspberry Pi bootable image files and articles/presentations about PiFlash.

=head2 Bug reporting

Report bugs via GitHub at L<https://github.com/ikluft/piflash/issues>

When reporting a bug, please include the full output using the --verbose option. That will include all of the
program's state information, which will help understand the bigger picture what was happening on your system.
Feel free to remove information you don't want to post in a publicly-visible bug report - though it's helpful
to add "[redacted]" where you removed something so it's clear what happened.

For any SD card reader hardware which piflash fails to recognize (and therefore refuses to write to),
please describe the hardware as best you can including name, product number, bus (USB, PCI, etc),
any known controller chips.

=head1 AUTHOR

Ian Kluft <cpan-dev@iankluft.com>


This software is Copyright (c) 2017-2019 by Ian Kluft.

This is free software, licensed under:

  The Apache License, Version 2.0, January 2004