http://qs321.pair.com?node_id=850303

skazat has asked for the wisdom of the Perl Monks concerning the following question:

Hello everyone,

I've just thrown together a web-based installer for a web-app of mine. This basically comes down to making a web-based application that takes in a bunch of information, via a form, validates it and takes that information to build the necessary directory structure, config file, SQL database - that sort of thing.

My users aren't going to know what an, "ssh" is, or a, "cpan", etc. They're expecting to untar a distro, throw it up, run, "install.cgi" and fill out a bunch of stuff. My other task is to make that, "bunch of stuff" easy as possible for them to fill out - or only fill out, if it's optional. Whew!

Something you see a lot in php, not so much in Perl.

I'm at the point where I'm happy that it works, etc, but I'm not so happy with how it interacts with the file system. I'm worried about security and want to make this thing work as well as possible, on as many unix-like platforms as possible. I'm also worried about file permission problems with my web-app and the file structure it needs to manipulate. For the most part, I'm assuming the CGI script is running under suEXEC.

For the most part, when I need to mv, cp, rm, (etc) something, I fall down and use backticks. Once I do, a big red flag goes up in the back of my mind - it doesn't sound like Best Practices to me and before I actually and truly ship this app, I want to replace the backticks with built-ins or subs from a module.

Some of the parameters I need for any method is it being either a core module, a pure-perl module or, built-in. Here's a small list of things I'm thinking of using, instead:

mv - File::Copy cp - File::Copy rm - unlink built-in chmod - chmod built-in

I'm a little worried that my use of Perl subroutines, instead of backticks are going to get me into trouble - will I have the same permissions in the directory structure using builtins/modules as I do with my backticks? Any best practices/nuggets of wisdom I should follow?

The other, strangely simple, yet strangely difficult thing to know is that web-apps own URL. Currently, I just use CGI.pm's url() sub, but this also will fail from time to time. Is there an alternative?

There's also quite a bit of file/directory munging and right now, this isn't handled very smartly - File::Spec isn't even being utilized. I was looking at Path::Class to handle this. Something as simple as wanting to know the absolute path, or the user's home directory aren't handled by anything I know in a standardizes way - I don't know if this is a security measure or what. It makes making something like this somewhat aggravating :)

Sorry to have to ask so many, seemingly simple questions - the installer works, but it doesn't work in a way I would be proud to show the world how Perl does things. I'm sure compiling a list of best-practices for web-app installers would help others down the road.

-skazat

Replies are listed 'Best First'.
Re: web-based installer wanderings
by ahmad (Hermit) on Jul 19, 2010 at 20:47 UTC

    It's been long time since I've seen anyone running without suEXEC, specially on a shared host.

    I think you're doing just fine.

    If you want to know where the script is currently stored/running from you can use $ENV{SCRIPT_FILENAME} for Apache or $ENV{PATH_TRANSLATED} for IIS.

    If you want to get the http url you can get it like this my $URL = 'http://' . $ENV{HTTP_HOST} . $ENV{SCRIPT_NAME}; for both web servers.

    You may want to take a look at the environment variables and test it out to see what works best for you.

    #!/usr/bin/perl -w use strict; use CGI; my $q = CGI->new; print $q->header; while ( my ($k,$v) = each %ENV ) { print "$k = $v <br />\n"; }
Re: web-based installer wanderings
by sierpinski (Chaplain) on Jul 19, 2010 at 19:16 UTC
    I always thought that the major reason to use modules like File::Copy was to make the script as platform-independent as you could. I don't see anything wrong with using backticks for system commands. I mean to run something (natively) on the host OS, you need either system, exec, or backticks, and they each have different functionality, but which one to use depends on what you want to accomplish.

    More than using backticks, I'd be concerned about the paths that you're using in the move and copy statements. Are you sure that the target directories will be there? Are you using mkdir -p where applicable? That kind of thing.

    Regarding 'rm', Perl has an 'unlink' function that operates the same way as the system call, with probably less overhead of the backticks, though that's just a guess. I've never had to worry about performance at this point.

    It sounds like you just need to do a bunch of idiot-checking before you execute certain lines of code, perhaps like checking to make sure $HOME is defined (or that your home directory exists) or whatever else before you proceed with your execute. In order to make any program work, you have to make some assumptions about the system it will be executed on. Even if you make an 'INSTALL' file that simply lists what the requirements are, if someone tries to use it that doesn't have suEXEC on, you can at least point out that it is a requirement, then at least they know what to ask for when they contact tech support (you?) for assistance.

    Hope that helps.
Re: web-based installer wanderings
by intel (Beadle) on Jul 20, 2010 at 02:23 UTC
    A great way to work with filesystems in Perl is to use globbing to gather filenames and filesystem information with a Perl built-in.
    @files = <*>; foreach $file (@files) { print $file . "\n"; }
    I know, it looks like an IO operation because of the <> but that's only true when a bareword like INPUT or a single-value scalar is used. Otherwise it is interpreted as a glob on the filesystem. You can also use the File::Glob module, which has a few more options and a little more functionality. Either way, this might be that "smarter way" that you were looking for of munging directories and finding the users' home directory/absolute path.
    ## glob on all files in home directory use File::Glob ':globally'; my @sources = <~gnat/*>;
    Hope that helps!

    <UPDATE> I also found some great info from this howto that specifically addresses backticks:
    http://sial.org/howto/perl/backticks/

Re: web-based installer wanderings
by aquarium (Curate) on Jul 19, 2010 at 23:23 UTC
    Sounds Ok so far. Have you considered using a installer package instead of writing your own? On unix it's usually OK to ask application owners to run setup/upgrade from command line with prerequisite guid or such, and have a bit of early sanity code check that you have required permissions.
    the hardest line to type correctly is: stty erase ^H
Re: web-based installer wanderings
by DrHyde (Prior) on Jul 20, 2010 at 09:46 UTC
    Anything that your script runs has the same permissions or fewer than the script itself (ignoring anything that has the setuid/setgid flag, which none of cp/rm/mv/chmod should have), so using modules instead of `cp` etc will be fine.
Re: web-based installer wanderings
by ruzam (Curate) on Jul 20, 2010 at 16:21 UTC

    Not quite the same approach, but a project I've created is something along the same lines. My application framework contains a built in tool to automatically bundle up the entire application into a single self contained 'install.pl' script. Build a web app, run the 'package' command, give to the world.

    My installer is command line based at the moment. When run it self unpacks, checking for app requirements (the installer is core pure perl, but the web app itself may have other requirements), creating directory structures and files, setting owner/groups and permissions. It also walks through creating databases if required (MySQL at the moment - db and admin user), generating an initial app config file and sample config files that can be copied more or less verbatim into a webserver setup. Some day I hope to expand it to swing both ways so the user has a choice of either running from a shell, or from a browser (ala php). I think that makes me the other side of your coin, so I hope we can learn something from each other :)

    It's a work in progress, but you can check out my installer here: Aqualung Sample Web App

    By all means PM me if you want to talk more.