The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

fetchware - Fetchware is a package manager for source code distributions.

VERSION

version 1.005

SYNOPSIS

Manpage synopsis.

    fetchware [-v | --verbose] [-q | --quiet] [-h | -? | --help]
              [-V | --version] <command> [<filenames | paths | Fetchwarfiles>]

Create a new fetchware package.

    fetchware new <name of program>

    ... Read the printed explanations...

    ... And answer the questions fetchware asks you appropriately and then press
    enter.

Install a new fetchware package.

    fetchware install name-of-program.Fetchwarefile

    # And you can use a .fpkg fetchware package instead of a Fetchwarefile if
    # you have one.
    fetchware install name-of-program.fpkg

Upgrade a specific fetchware package.

    fetchware upgrade <name of already installed program>

    # Use fetchware list to see a list of already installed programs.
    fetchware list

Upgrade all installed fetchware packages.

    fetchware upgrade-all

Uninstall an installed fetchware package.

    # Requires a "uninstall" make target, or customization of its Fetchwarefile
    # to specify what specific C<uninstall_commands> will uninstall this package.

List all installed fetchware packages.

    fetchware list 

    # Pipe to grep if you want to search for something specific.
    fetchware list | grep <something specific>

"Look" inside a fetchware package.

    fetchware look <name-of-program.fpkg> | <name-of-program.Fetchwarefile>

Put this in your /etc/cron.daily to make fetchware check for updates every night

    #!/bin/sh
    # Update all already installed fetchware packages.
    fetchware upgrade-all

Or use crontab -e to put this in a user crontab if you don't want to fetchware system wide

    # Check for updates using fetchware every night at 2:30AM.
    # Minute   Hour   Day of Month     Month          Day of Week     Command    
    # (0-59)  (0-23)     (1-31)  (1-12 or Jan-Dec) (0-6 or Sun-Sat)
        30      2          *              *               *           fetchware upgrade-all

DESCRIPTION

Fetchware is a package manager for source code distributions. It takes advantage of the fact that coincidentially most source code distributions follow the same conventions. Most use FTP and HTTP mirrors. Most use AutoTools or at least just a few commands that you execute in sequence to configure, build, and install the program.

Fetchware harnesses these conventions to create a powerful and flexible package manager for source code distributions. It includes a simple, powerful, and flexible configuration syntax stored in files called Fetchwarefiles. These Fetchwarefiles specify the required mandatory configuration options, program, lookup, mirror, and a method of verifying your program. And they also specify any additional optional configuration options.

To create a new Fetchwarefile to install a source code distribution use the fetchware new command. It will ask you a bunch of questions, and based on your answers and fetchware's assumptions fetchware will automagically create a new Fetchwarefile for you. Then it will ask if you would like fetchware to install it for you.

If your source code distribution exceeds fetchware's new command's capabilities, then see the section "MANUALLY CREATING A App::Fetchware FETCHWAREFILE" in App::Fetchware. It details how to create a Fetchwarefile manually in a text editor of your choice.

Fetchware's commands are described next followed by its options. Following that is the section "HOW FETCHWARE WORKS", which describes in some detail how Fetchware does its magic, and documents how it all fits together.

See App::Fetchware for more information on fetchware's Fetchwarefile syntax:

COMMANDS

Each command maps to one operation a package manager can do. install, upgrade, and uninstall. There is also new to create new Fetchwarefiles without bothering with a text editor. And fetchware's way of upgrading all packages with upgrade-all. Fetchware can also list its installed packages with list. And look is similar to Perl's original CPAN client's look command that downloads and unarchives the package, so you can "look" at it.

new

    fetchware new <name of program>

new asks you a bunch of questions, and uses the answers you provide in addition to the contents of the directory listng fetchware downloads based on the lookup_url you give fetchware, to create a Fetchwarefile for you with all the mandatory options filled in. It also gives you the opportunity to add any additional options that you may want to use. new also gives you a chance to edit the Fetchwarefile it created for you manually in your editor. Set the EDITOR environment variable to pick which editor to use, or leave it empty, and fetchware will ask you what editor you would like to use.

new finishes by asking if you would like fetchware to go ahead and install the Fetchwarefile it has just created for you. If you say yes, then fetchware will install it, or if you say no, fetchware will skip installing it for you, and print out the path to the Fetchwarefile it just created for you.

You can install that Fetchwarefile later with:

    fetchware install path/to/your/some-program.Fetchwarefile

For more details about fetchware's configuration files Fetchwarefiles see "CREATING A App::Fetchware FETCHWAREFILE" in App::Fetchware

install

    fetchware install <path to program.Fetchwarefile>

    fetchware install <path to program.fpkg>

install parses the given Fetchwarefile or uses the embeded Fetchwarefile inside the fetchware package you specify. Then install installs your program as you specified in your Fetchwarefile.

By default executes the commands:

1. ./configure
2. make
3. make install

You can use the Fetchwarefile configuraton options build_commands to specify alternate commands to build the program replacing ./configure and make, and you can also specify the install_commands to replace make install with some other command or commands that install your program.

    ...
    # build_commands and install_commands Fetchwarefile example.
    build_commands './Configure', 'make';

    install_commands 'make test', 'make install';

    ...

See ""App::Fetchware'S FETCHWAREFILE CONFIGURATION OPTIONS" in App::Fetchware for more details on these configuration options.

upgrade

    fetchware upgrade <already installed fetchware package>

upgrade only upgrades already installed fetchware packages. You cannot upgrade a Fetchwarefile only an already installed fetchware package. To see a list of already installed fetchware packages run fetchware list, or pipe it through grep(1)

    fetchware list | grep <keyword>

upgrade-all

    fetchware upgrade-all

upgrade-all takes no arguments. Instead, it loops over the list of installed programs fetchware list and runs upgrade on each one to upgrade all currently installed programs.

uninstall

uninstall removes all components of a currently installed program. Afterwards, that program won't show up in a fetchware list anymore.

WARNING

uninstall is only capable of uninstalling programs that maintain a uninstall make target. For example, ctags has a make uninstall, while Apache does not; therefore, without a prefix, fetchware can uninstall ctags, but it cannot uninstall Apache.

The easiest way to be able to uninstall a program you install with fetchware that does not have a make uninstall is to use the prefix configuration option to use a separate prefix that everything is installed into this directory. Then you could specify a custom uninstall_commands that would delete everything in that directory:

    # Set prefix so apache can be easily uninstalled.
    prefix '/usr/local/apache';

    # Set uninstall_commands to delete everything in the prefix directory when
    # apache is uninstalled.
    uninstall_commands 'rm -r /usr/local/apache';

Then when you uninstall apache, fetchware deletes its associated files, which may include your Web site's Web files, so back them up before hand if you need to keep them.

The other way around this limitation is to use one of the following programs that use a cool LD_PRELOAD trick to watch what files make install or its equivelent copy, and where they are copied to. Then these files are put into some sort of vendor-specific package such as apt-get or rpm.

checkinstall

Run like checkinstall make install will detect what files are copied where during installation, and will create a slackware, debian, or redhat package based on this information.

paco

Provides very similar functionality to fetchware, but lacks fetchware's lookup and verify mechanisms. Includes its own package management functionality.

As far a fetchware one day supporting some sort of hack like checkinstall or paco use, I'm against it. I'd prefer everyone just adding a make uninstall to their Makefiles. But it is on my todo list, and I may add similar functionality in the future, but I'll make no promises. Until then consider using the prefix and uninstall_commands hack.

list

    fetchware list

    fetchware list | grep <what are you looking for?>

list just prints out the names of all fetchware packages that have been installed. It takes no arguments, and currently does not support listing only packages that match a certain criteria. However, you can just pipe it to grep(1) to using a regex to limit which packages you're looking for.

look

    fetchware look <package name>

look looks up the specified program using your lookup_url, downloads it, verifies it, and unarchives it. Then it prints out the location of the unarchived program, so you can take a look at its code, or install it manually if you would like to.

clean

    fetchware clean

clean deletes all fetchware temporary files and directories to clean up your system temporary directory.

You can also specify one or more arguments to fetchware clean to specify what directories you want fetchware to search for fetchware's left over temp files to clean up.

help

Prints out a brief screen full of help messages reminding you of fetchware's command-line syntax.

OPTIONS

Fetchware's configuration file options are detailed below.

Most of its options are stored in its configuration file. If none of these options suite what you need fetchware to do, consider using its Fetchwarefile to meet your needs. See ""App::Fetchware'S FETCHWAREFILE CONFIGURATION OPTIONS" in App::Fetchware

-v or --verbose

    fetchware -v install <some-program.Fetchwarefile>

Fetchware's -v or --verbose option turns on verbose logging, which prints to STDOUT additional information regarding what fetchware is doing and how fetchware does it.

If you have any problems with your Fetchwarefile, then you could turn on verbose mode to have fetchware log additional messages to STDOUT to aid in debugging your Fetchwarefile.

-q or --quite

    fetchware -q upgrade <some-program>

The -q or --quite option tells fetchware to not log anything at all. Fetchware will even prevent any commands it runs from printing output to your terminal's STDOUT to avoid cluttering up your screen.

Any warnings or error messages are still printed to STDERR.

To determine if fetchware succeeded or failed you can test its exit status:

    fetchware -q upgrade <some-program>

    echo $?
    0

Fetchware exits 0 for success and non-zero for failure.

-V or --version

Prints out a short message and says what version of fetchware is running.

-h or -? or --help

Prints out a brief screen full of help messages reminding you of fetchware's command-line syntax.

HOW FETCHWARE WORKS

Fetchware works by having fetchware, the bin/fetchware file and fetchware Perl package, do all of the "package manager" stuff:

  • Creating fetchware packages (create_fetchware_package())

  • Copying fetchware packages to the fetchware database (copy_fpkg_to_fpkg_database())

  • Creating and managing the fetchware database (determine_fetchware_package_path(), extract_fetchwarefile(), and fetchware_database_path())

  • uninstalling installed packages from the fetchware database (uninstall_fetchware_package_from_database())

Fetchware delegates all of the specifics on how to install, upgrade, and uninstall the fetchware packages that fetchware manages to App::Fetchware or a App::Fetchware extension:

  • Lookup to see if a new version is available (lookup())

  • Downloading the archive (download())

  • Verifying that the downloaded file is the same one the author uploaded (verify())

  • Unarchiving the package (unarchive())

  • Building and installing it (build() and install())

  • Uninstalling any already installed fetchware package (uninstall())

  • Some before and after hooks (start() and end()).

How fetchware's commands work

Fetchware's commands work by using fetchware's API, described in the section "INTERNAL LIBRARY SUBROUTINES", to manage the package manager stuff. And fetchware delegates the heavy lifting of the steps needed to install, upgrade, and uninstall fetchware packages to App::Fetchware or a App::Fetchware extension.

new

new just asks the user a bunch of questions, and gives them an opportunity to answer questions. Then it uses your answers to generate a Fetchwarefile for you, so that you don't have to mess with creating one manually in a text editor.

install

Fetchware's install runs whatever fetchware API subroutines it needs to use, see the section "INTERNAL LIBRARY SUBROUTINES" for more. Then, install() will parse a user provided Fetchwarefile or a Fetchwarefile fetchware finds in a fetchware package. The act of parsing the Fetchwarefile will import the App::Fetchware API subroutines into fetchware's namespace. This gives fetchware access to App::Fetchwares API or whatever extension may have been used. Then, the API subroutines are run providing whatever arguments they need and storing whatever their important return values may be in a variable to probably later be given to a later API subroutine as an argument.

upgrade

Upgrade just cleverly calls the same subroutine that implementes install. And if the version install determines is the lastes is the same as the version that is already installed, then fetchware does not bother installing it again.

uninstall

Uninstall parses the Fetcwharefile of the installed pacakge you specified. Then it runs whatever uninstall_commands you specified or the default, make uninstall if you specified none. Then the installed package is deleted from the fetchware database.

list

List just globs all files in the fetchware database directory as returned by fetchware_database_path(), and prints them to STDOUT. It does not let you specify a Perl regex, or a keyword or anything yet, because I'm currently unsure about the security ramifications of doing so. This feature may be added in the future.

look

look just does the first part of install(). It parses whatever Fetchwarefile it gets passed to it, then it does the start(), lookup(), download(), verify(), and unarchive() parts of install(). Then look prints the path of this directory, and exits.

clean

Clean just deletes all fetchware temp files and directories in the system temp_dir. These files and directories all start with fetchware-* or Fetchwarefile-*.

help

Just prints a simple, shirt, concise help message.

How fetchware interfaces with App::Fetchware

Fetchware interfaces with App::Fetchware using the parse_fetchwarefile() API subroutine. This subroutine simply eval()'s your Fetchwarefile and traps any errors, and then rethrows that exception adding a helpful message about what happened in addition to passing along the original problem from Perl.

The act of eval()ing your Fetchwarefile causes Perl to parse and execute as it would any other Perl program. Only because its inside an eval any subroutines that are imported are imported in the the caller of eval()'s package. In this case fetchware.

Fetchware takes advantage of this by requiring all Fetchwarefile's to have a use App::Fetchware...; line. This line is what imports the default imports of App::Fetchware into fetchware, which include App::Fetchware's API subroutines.

How fetchware intefaces with a fetchware extension

As explained above parse_fetchwarefile() eval()'s your Fetchwarefile, and this causes Perl to parse and execute it. And any imports are imported into the caller's package, which is fetchware.

That's how fetchware receives App::Fetchware's API subroutines, and it is also how fetchware receives a fetchware extensions API subroutines, the fetchware extension is simply use()d inside your Fetchwarefile instead of the default one of App::Fetchware. Instead of:

    use App::Fetchware;

You would write:

    use App::FetchwareX::HTMLPageSync;

To use the fetchware extension HTMLPageSync.

INTERNAL SUBROUTINES IMPLEMENTING FETCHWARE COMMANDS

Below are all of subroutines that implement fetchware's main command line options such as fetchware install or fetchware new and so on. These main subroutines are called based on the options you pass to fetchware from the command line.

cmd_install()

    my $installed_fetchware_package_path = cmd_install($filename|@ARGV)

cmd_install() implements fetchware's install command, which installs a package based on the specified Fetchwarefile or fetchware package.

cmd_uninstall()

    my $uninstall_package_path = cmd_uninstall($uninstall_package_path|@ARGV);

Uninstalls the given package. Note the given package does not have to be an exact match, but it does have to be unique if you have two versions of the same software installed such as httpd-2.2 and httpd-2.4. In that case you'd have to specify the version number as well.

LIMITATION

cmd_uninstall() unlike cmd_install() does not accept Fetchwarefiles as an argument to uninstall a fetchware package! Instead, you must provide the name and perhaps the name and version number of an already installed software package. For a list of such package names just run fetchware list to list all installed fetchware packages.

NOTICE

cmd_uninstall() does not call drop_privs() to drop privileges, because it needs root privileges to copy the installed fetchware package from the system level fetchware package database, and it needs root to actually be able to delete files in system level directories.

cmd_new()

    my $fetchware_package_path = cmd_new($program_name);

cmd_new() implements fetchware's new command. See "CREATING A App::Fetchware FETCHWAREFILE" in App::Fetchware for detailed documentation for the specifics of the new command. This chunk of POD is about its implementation. cmd_new() calls a bunch of helper subroutines that implement the algorithm fetchware uses to build new Fetchwarefiles automagically for the user. The algorithm is dead stupid:

1. Ask for lookup_url & download it.
2. Analyze the contents of the output from the lookup_url.
3. Build the Fetchwarefile according to the output.
4. Ask other questions as needed.

cmd_new() uses Term::UI, which in turn uses Term::ReadLine to implement the character based question and anwser wizard interface.

cmd_new() also asks the user if they would like fetchware to build and install their new program based on their newly created Fetchwarefile. If they answer yes, it builds and installs it, and if not, cmd_new() returns the path to the created Fetchwarefile for them.

cmd_new() API REFERENCE

Below are the API routines that cmd_new() uses to create the question and answer interface for helping to build new Fetchwarefiles and fetchware packages.

name_program();

    my $program_name = name_program($term);

Asks the user to provide a name for the program that will that corresponds to Fetchwarefile's program configuration subroutine. This directive is currently not used for much, but might one day become a default filter option, or might be used in msg() output to the user for logging.

opening_message();

    opending_message();

Prints new()'s opening message. It takes no parameters including the message to be printed; instead, it simply prints the message only it can access to STDOUT.

get_lookup_url()

    my $lookup_url = get_lookup_url($term);

Uses $term argument as a Term::ReadLine/Term::UI object to interactively explain what a lookup_url is, and to ask the user to provide one and press enter.

download_lookup_url()

    my $filename_listing = download_lookup_url($term, $lookup_url);

Attempts to download the lookup_url the user provides. Returns it after parsing it using parse_directory_listing() from App::Fetchware that lookup() itself uses.

analyze_lookup_listing()

    analyze_lookup_listing($term, $filename_listing, $lookup_url, $fetchwarefile);

Calls numerous subroutines to analyze the $filename_listing to determine what mandatory configuration file options should be, and if any optional configuration file options may be needed such as filter.

The helper subroutines that analyze_lookup_listing() calls must follow the proper API. These helper subroutines are called, and are expected to use their $fetchwarefile argument to call append_options_to_fetchwarefile() directly to directly add their options to the user's $fetchwarefile.

determine_mandatory_options()

    my $filter = determine_mandatory_options($term, $filename_listing, $lookup_url);

Analyzes $filename_listing and asks the user the necessary questions to determine what the values to fetchware's mandatory configuration options should be. These mandatory configuration options are: program, lookup_url, mirror. And some method of verifying any archives that fetchware downloads using one or more of gpg_keys_url, sha1_url or md5_url, or using verify_failure_ok to disable verifying downloaded archives, which is not recommended.

Note: program and lookup_url were defined earlier, so determine_mandatory_options() only determines mirrors and how to verify downloads. It uses add_mirrors() and add_verification() to do this.

add_mirrors()

    add_mirrors($term, $filename_listing, $fetchwarefile);

Asks the user to specify at least one mirror to use to download their archives. It also reiterates to the user that the lookup_url should point to the author's original download site, and not a 3rd party mirror, because md5sums, sha1sums, and gpg signatures should only be downloaded from the author's download site to avoid them being modified by a hacked 3rd party mirror. While mirror should be configured to point to a 3rd party mirror to lessen the load on the author's offical download site.

After the user enters at least one mirror, add_mirrors() asks the user if they would like to add any additional mirrors, and it adds them if the user specifies them.

add_mirrors() then uses append_options_to_fetchwarefile() to add these options to the user's $fetchwarefile.

add_verification()

    add_verification($term, $filename_listing, $lookup_url, $fetchwarefile);

Parses $filename_listing to determine what type of verification is available. Prefering gpg, but falling back on sha1, and then md5 if gpg is not available.

If the type is gpg, then add_verification() will ask the user to specify a gpg_keys_url, which is required for gpg, because fetchware needs to be able to import the needed keys to be able to use those keys to verify package downloads. If this URL is not provided by the author, then add_verification() will ask the user if they would like to import the author's key into their own gpg public keyring. If they would, then add_verification() will use the user_keyring 'On' option to use the user's public keyring instead of fetchware's own keyring. And if the user does not want to use their own gpg public keyring, then add_verification will fall back to sha1 or md5 setting verify_method to sha1 or md5 as needed.

Also, adds a gpg_keys_url option if a KEYS file is found in $filename_listing.

If no verification methods are available, fetchware will print a big nasty warning message, and offer to use verify_failure_ok to make such a failure cause fetchware to continue installing your software.

Adds all of the determined options to $fetchwarefile using append_options_to_fetchwarefile().

determine_filter_option()

    determine_filter_option($term, $filename_listing, $fetchwarefile);

Analyzes $filename_listing and asks the user whatever questions are needed by fetchware to determine if a filter configuration option is needed, and if it is what it should be. filter is simply a perl regex that the list of files that fetchware downloads is checked against, and only files that match this regex will fetchware consider to be the latest version of the software package that you want to install. The filter option is needed, because some mirrors will have multiple software packages in the same directory or multitple different versions of one piece of software in the same directory. An example would be Apache, which has Apache versions 2.0, 2.2, and 2.4 all in the same directory. The filter option is how you differentiate between them.

If a filter option was provided append_options_to_fetchwarefile() is used to append the provided filter option to the user's $fetchwarefile.

append_to_fetchwarefile()

    append_to_fetchwarefile(\$fetchwarefile, $config_file_option, $config_file_value, $description)

Turns $description into a comment as described below, and then appends it to the $fetchwarefile. Then $config_file_option and $config_file_value are also appended inside proper Fetchwarefile syntax.

$description is split into strings 78 characters long, and printed with # prepended to make it a proper comment so fetchware skips parsing it.

$description is optional. If you do not include it when you call append_to_fetchwarefile(), then append_to_fetchwarefile() will not add the provided description.

NOTE Notice the backslash infront of the $fetchwarefile argument above. It is there, because the argument $fetchwarefile must be a reference to a scalar.

prompt_for_other_options()

    prompt_for_other_options($term);

Asks user if they would like to add any other options to their Fetchwarefile. If they answer no, then everything else this subroutine does is skipped. If they answerer yes, then information helping them decide what to do is printed as needed. They are asked to input space separated list of configuration options they would like to customize. Then for each option they specify, a helpful message is printed that will help them determine what they should provide for that option, and then they input what they would like to answer for that option.

The user's answers are tallied up in a hash that is returned as a list instead of as a hash reference.

append_options_to_fetchwarefile()

    append_options_to_fetchwarefile(\%options, \$fetchwarefile);

Takes a hash ref of name value fetchwarefile configuration options and a scalar ref representing your Fetchwarefile, and then it appends your fetchwarefile configuration options to the provided $fetchwarefile scalar ref. It also prepends a description of that specific fetchwarefile option.

If you provide an configuration option for which append_options_to_fetchwarefile() does not have a preexisting description for in its internal hash, %config_file_description, append_options_to_fetchwarefile() will throw an exception.

append_options_to_fetchwarefile() uses append_to_fetchwarefile() to do the manipulation of your $fetchwarefile.

edit_manually()

    $fetchwarefile = edit_manually($term, \$fetchwarefile);

edit_manually() asks the user if they would like to edit the specified $fetchwarefile manually. If the user answers no, then nothing is done. But if the user answers yes, then fetchware will open their favorit editor either using the $ENV{EDITOR} environment variable, or fetchware will ask the user what editor they would like to use. Then this editor, and a temporary fetchwarefile are opened, and the user can edit their Fetchwarefile as they please. If they are not satisfied with their edits, and wan to undo them, they can delete the entire file, and write a size 0 file, which will cause fetchware to ignore the file they edited. If the write a file with a size greater than 0, then the file the user wrote, will be used as their Fetchwarefile.

check_fetchwarefile()

    check_fetchwarefile($fetchwarefile);

Ensures that the fetchwarefile that fetchware new creates has all of the necessary configuration options that fetchware requires. These are:

  • program - A name for your Fetchwarefile. If they're all called Fetchwarefile how do you tell them apart?

  • lookup_url - need a url to determine what the latest version is.

  • mirror - need a 3rd party mirror to avoid drowning the main mirror with exceess requests.

  • Some method of verification. Checks for gpg_keys_url, and if it or user_keyring are not found, then a warning is given. Gpg is the only recommended way of verifying downloads.

Throws an exception if any of the conditions above are not met.

ask_to_install_now_to_test_fetchwarefile()

    my $fetchware_package_path = ask_to_install_now_to_test_fetchwarefile($term, \$fetchwarefile, $program_name);
    my $fetchwarefile_filename = ask_to_install_now_to_test_fetchwarefile($term, \$fetchwarefile, $program_name);

This subroutine asks the user if they want to install the Fetchwarefile that this subroutine has been called with. If they say yes, then the Fetchwarefile is passed on to cmd_install() to do all of the installation stuff. If they say no, then fetchware saves the file to "$program_name.Fetchwarefile" or ask_to_install_now_to_test_fetchwarefile() will ask the user where to save the file until the user picks a filename that does not exist.

If you answer yes to install your Fetchwarefile, then ask_to_install_now_to_test_fetchwarefile() will return the full path to the fetchware package that has been installed.

cmd_upgrade()

    my $installed_fetchware_package_path = cmd_upgrade($upgrade_name);
    'No upgrade needed.' = cmd_upgrade($upgrade_name);

Subroutine implementing Fetchware's upgrade command. This subroutine and command upgrade one and only one package that must be specified on the command line as well.

cmd_upgrade_all()

    my @upgraded_packages = cmd_upgrade_all();
    'No upgrade needed.' = cmd_upgrade_all();

Implements the fetchware upgrade-all command, which upgrades all installed packages simply by looping over the fetchware database and running cmd_upgrade() on each one.

Returns a list of the packages that were upgraded or the string 'No upgrade needed.' if no packages were upgraded.

cmd_look()

    my $look_path = cmd_look($filename);

Looks up the latest version of the specified Fetchwarefile or fetchware package, and downloads, verifies, and unarchives the specified source code distribution, and then prints out the location of this archive.

LIMITATION

cmd_look() unarchive's the desired source code distribution into the same sort of temporary directory that fetchware itself uses during regular installs or upgrades. This cannot be changed, but after fetchware creates this directory it outputs its path, so that you can cd to it, and do whatever you need to it. You could also move it to where you want it to be as well. Remember to delete the fetchware-$PID-randomeletters style directory that it was stored in, or just run fetchware clean when you are finished working with it.

cmd_list()

    cmd_list();

Lists all of the packages fetchware has stored in its fetchware_database_path().

LIMITATION

There is no ability to limit this listing with a regex currently, so just pipe it to grep for now. Obviously in the future this ability could be added, but I'm currently unclear about its security ramifications. So for now, I'll hold out until I study what ack does.

cmd_clean()

    cmd_clean(@ARGV);

cmd_clean() implements fetchware's clean command, which deletes any left over fetchware temporary directories from your system's temorary directory. It cleverly uses locking to ensure that cmd_clean() does not delete a temporary directory that is still being used by a running fetchware process.

cmd_clean() also deletes any temporary files that Fetchware uses that are regular files not directories. These start with either fetchware-* or Fetchwarefile-* for Fetchwarefiles cmd_new() creates for the user.

flock() is used along with LOCK_{EX,NB} from Fcntl. LOCK_EX gets an exclusive lock (only current process who got lock can access the file, and LOCK_NB, which does a non-blocking attempt to get a lock returning success at getting the lock or not getting the lock immediately. flock() is used on a semaphore file called fetchware.sem it is a useless empty file, that is only used for locking each fetchware temporary directory.

flock() is used, because if the fetchware process using the lock closes the file or the process dies, exits itself, or is killed even sith SIGKILL, the lock is released automatically by the OS and/or system libraries.

cmd_clean() simply attempts to get a lock, and if it does it deletes that particular fetchware temporary directory. If it fails to get the exclusive lock, then it probably means that that fetchware temporary directory is still being used by another fetchware process, so that directory is skipped.

create_tempdir() and cleanup_tempdir() create and lock the fetchware semaphore lock file, and close and unlock it as they are executed by start() and end().

cmd_clean() via @ARGV, which run() calls it with, takes the arguments it receives as paths to whatever temporary directories it should clean.

cmd_help()

    cmd_help();

Prints a help message to STDOUT listing usage, all command options, and examples.

And then exit()s with an exit status of 0 indicating success.

INTERNAL LIBRARY SUBROUTINES

Below are the helper subroutines used by install(), uninstall(), new(), and so on.

parse_fetchwarefile()

    'Evaled config file successfully' = parse_fetchwarefile(\$fetchwarefile);

Eval's the \$fetchwarefile to effectively "parse" it.

The only checking for the $fetchwarefile it does is that it is a scalar ref, and that it has at least one line beginning with use App::Fetchware.

Returns true on success and dies with an error message if it fails.

create_fetchware_package()

    # Most uses should just use this.
    my $fetchware_package_full_path
        =
        create_fetchware_package($fetchwarefile, $unarchived_package_path);


    # But some uses in test suites thanks to safe_open() need to be able to
    # specify where they should write the new fetchware package's path to.
    my $fetchware_package_full_path
        =
        create_fetchware_package($fetchwarefile,
            $unarchived_package_path
            $path_to_new_fpkg);

Creates a fetchware package, ending in .fpkg, using $unarchived_package_path, as the directory to archive. Also, adds the Fetchwarefile stored in the scalar $fetchwarefile argument to the fethware package that is created.

You can specify an optional $dir_for_new_fpkg, which will be a directory where create_fetchware_package() will write the new fetchware package to.

Returns the full pathname to the fetchware package that was created.

fetchware_database_path()

    my $fetchware_database_path = fetchware_database_path();

Returns the correct path for the fetchware package database based on operating system and if super user or not.

Also, supports user customizable fetchware database paths via the FETCHWARE_DATABASE_PATH environment variable, and the fetchware_database_path Fetchwarefile configuration file. If both are specified fetchware_database_path is prefered over FETCHWARE_DATABASE_PATH.

determine_fetchware_package_path()

    my $fetchware_package_filename = determine_fetchware_package_path($fetchware_package);

Looks up the $fetchware_package in fetchware_database_path(), and returns the full path to that given $fetchware_package.

extract_fetchwarefile()

    my $fetchwarefile = extract_fetchwarefile($fetchware_package_path);

Extracts out the Fetchwarefile of the provided fetchware package as specified by $fetchware_package_path, and returns the content of the Fetchwarefile as a scalar reference. Throws an exception if it it fails.

copy_fpkg_to_fpkg_database()

    my $fetchware_package_path = copy_fpkg_to_fpkg_database($fetchwarefile_path);

Installs (just copies) the specified fetchware package to the fetchware database, which is /var/log/fetchware on UNIX, C:\FETCHWARE on Windows with root or Administrator. All others are whatever File::HomeDir says. For Unix or Unix-like systems such as linux, File::HomeDir will put your own user fetchware database independent of the system-wide one in /var/log/fetchware in ~/.local/share/Perl/dist/fetchware/. This correctly follows some sort of standard. XDG or FreeDesktop perhaps?

Creates the directory the fetchware database is stored in if it does not already exist.

Returns the full path of the copied fetchware package.

uninstall_fetchware_package_from_database()

    my uninstall_fetchware_package_from_database($uninstall_package_name);

Deletes the specified $uninstall_package_name from the fetchware package database. Throws an exception on error.

MOTIVATION

While sysadmining I liked to install my own compiled from source versions of popular programs like Apache, MySQL, or Perl without threading. However, doing so means that you have to manually recompile everytime a new security hole comes out, which is annoyingly frequent for Apache. So, fetchware was created to bring the power of package management to source code distributions.

THE FETCHWARE PACKAGE

Like other package managers, fetchware has its own package format:

  • It ends with a .fpkg file extension.

  • The package path, the location of the unarchived downloaded program, is simply archived again using Archive::Tar, and compressed with gzip.

  • But before the package path is archived the currently used Fetchwarefile is copied into the current directory, so that it is included with your fetchware package:

        ./Fetchwarefile
        httpd-2.2.x
        httpd-2.2.x/README
        httpd-2.2.x/INSTALL
        ....

This simple package format was chosen instead of using a native package format such as a MS .msi package, slackware format, rpm format, .deb format, and so on. Thanks to distros like Gentoo and Arch, there are even more formats now. Also, each version of BSD has its own package format, and each version of commerical UNIX has its own package format too. ...It was easier to create a new format, then deal with all of the existing ones.

This custom package format is unique, bare bones, and retains all of the power that installing the software from source manaully gives you.

  • Simple, and retains backward compatibility with manual installation.

  • The package format includes the source code, so it can be recompiled if you move the fetchware package to an architecture different than the one it was compiled on.

  • You can specify whatever configure and build options you want, so you're not stuck with whatever your distro's package maintainer has chosen.

FAQ

How does fetchware's database work?

The design of fetchware's database was copied after Slackware's package database design. In Slackware each package is a file in /var/log/packages, an example: /var/log/packages/coreutils-8.14-x86_64_slack13.37. And inside that file is a list of files, whoose names are the locations of all of the files that this Slackware package installed. This format is really simple and flexible.

Fetchware's database is simply the directory /var/log/fetchware (on Unix when run as root), or whatever File::HomeDir recommends. When packages are installed the final version of that package that ends with .fpkg is copied to your fetchware database path. So after you install apache your fetchware database will look like:

    ls /var/log/fetchware
    httpd-2.4.3.fpkg

It's not a real database or anything cool like that. It is simply a directory containting a list of fetchware packages that have been installed. However, this directory is managed by fetchware, and should not be messed with unless you are sure of what you are doing.

What exactly is a fetchware package?

A fetchware package is a gziped tar archive with its file extension changed to .fpkg. This archive consists of the package that was downloaded in addition to your Fetchwarefile. For example.

    tar tvf httpd-2.4.3.fpkg
    ./Fetchwarefile
    httpd-2.4.3/README
    httpd-2.4.3/...
    ...

See the section "THE FETCHWARE PACKAGE" to see all of the cool things you can do with them.

ERRORS

As with the rest of App::Fetchware, fetchware does not return any error codes; instead, all errors are die()'d if it's fetchware's error, or croak()'d if its the caller's fault.

CAVEATS

WINDOWS COMPATIBILITY

Fetchware was written on Linux and tested by its author only on Linux. However, it should work on popular Unixes without any changes. But it has not been ported or tested on Windows yet, so it may work, or parts of it may work, but some might not. However, I have used File::Spec and Path::Class to support path and file manipulation accross all Perl-supported platorms,so that code should work on Windows. I intend to add Windows support, and add tests for Windows in the future, but for now it is unsupported, but may work. This is likely to improve in the future.

AUTHOR

David Yingling <deeelwy@gmail.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by David Yingling.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.