.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.13) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MNI::Startup 3" .TH MNI::Startup 3 "2000-02-21" "perl v5.10.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" MNI::Startup \- perform common startup/shutdown tasks .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use MNI::Startup; \& \& use MNI::Startup qw([optvars|nooptvars] \& [opttable|noopttable] \& [progname|noprogname] \& [startdir|nostartdir] \& [cputimes|nocputimes] \& [cleanup|nocleanup] \& [sig|nosig]); \& \& self_announce ([$log [, $program [, $args]]]); \& \& backgroundify ($log [, $program [, $args]]); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fIMNI::Startup\fR performs several common tasks that need to be done at startup and shutdown time for most long-running, computationally-intensive Perl scripts. (By \*(L"computationally-intensive\*(R" here I mean not that the script itself does lots of number crunching\-\-\-rather, it runs other programs to do its work, and acts to unify a whole sequence of lower-level computational steps. In other words, \fIMNI::Startup\fR is for writing glorified shell scripts.) .PP Each startup/shutdown task is independently controllable by a short \&\*(L"option string\*(R". The tasks, and the options that control them, are: .ie n .IP """progname""" 4 .el .IP "\f(CWprogname\fR" 4 .IX Item "progname" Split \f(CW$0\fR up into program name and directory. .ie n .IP """startdir""" 4 .el .IP "\f(CWstartdir\fR" 4 .IX Item "startdir" Get the starting directory and split off the last component (the \&\*(L"start directory name\*(R"). .ie n .IP """optvars""" 4 .el .IP "\f(CWoptvars\fR" 4 .IX Item "optvars" Initialize several useful global variables: \f(CW$Verbose\fR, \&\f(CW$Execute\fR, \f(CW$Clobber\fR, \f(CW$Debug\fR, \f(CW$TmpDir\fR, and \f(CW$KeepTmp\fR. .ie n .IP """opttable""" 4 .el .IP "\f(CWopttable\fR" 4 .IX Item "opttable" Create an option sub-table that can be incorporated into a larger option table for use with the \fIGetopt::Tabular\fR module. .ie n .IP """cputimes""" 4 .el .IP "\f(CWcputimes\fR" 4 .IX Item "cputimes" Keep track of elapsed \s-1CPU\s0 time and print it out at exit time (depending on certain other conditions). .ie n .IP """cleanup""" 4 .el .IP "\f(CWcleanup\fR" 4 .IX Item "cleanup" Clean up a temporary directory at exit time (depending on certain other conditions). .ie n .IP """sig""" 4 .el .IP "\f(CWsig\fR" 4 .IX Item "sig" Install a signal handler to cleanup and die whenever we are hit by certain signals. .PP By default, \fIMNI::Startup\fR does everything on this list (i.e., all options are true). Options are supplied to the module via its import list, and can be negated by prepending \f(CW\*(Aqno\*(Aq\fR to them. For instance, if you want to disable printing \s-1CPU\s0 times and signal handling, you could supply the \f(CW\*(C`nocputimes\*(C'\fR and \f(CW\*(C`nosig\*(C'\fR tokens as follows: .PP .Vb 1 \& use MNI::Startup qw(nocputimes nosig); .Ve .PP Note that having a particular option enabled usually implies two things: a list of variable names that are exported into your namespace at compile-time, and a little bit of work that \fIMNI::Startup\fR must do at run-time. Thus, you don't have the kind of fine control over selecting what names are exported that you do with most modules. The exact details of what work is done and which names are exported are covered in the sections below. .SH "PROGRAM NAME AND START DIRECTORY" .IX Header "PROGRAM NAME AND START DIRECTORY" The first two tasks done at run-time are trivial, but important for intelligent logging, useful error messages, and safe cleanup later on. First, \fIMNI::Spawn\fR splits \f(CW$0\fR up into the \*(L"program directory\*(R" (up to and including the last slash) and the \*(L"program name\*(R" (everything after the last slash). These two components are put into \f(CW$ProgramDir\fR and \&\f(CW$ProgramName\fR, both of which will be exported to your program's namespace if the \f(CW\*(C`progname\*(C'\fR option is true (which, like with all of \&\fIMNI::Startup\fR's options, is the default). If there are no slashes in \&\f(CW$0\fR, then \f(CW$ProgramDir\fR will be empty. .PP Next, if necessary, \fIMNI::Startup\fR gets the current directory (using \&\f(CW\*(C`Cwd::getcwd\*(C'\fR) and saves it in \f(CW$StartDir\fR; the last component of this directory is also extracted and saved in \f(CW$StartDirName\fR (hey, you never know when you might want it). This can be turned off by setting the \f(CW\*(C`startdir\*(C'\fR option to false; under certain obscure circumstances, though, \fIMNI::Startup\fR will decide that it really does need to know the startup directory and will call \f(CW\*(C`Cwd::getcwd\*(C'\fR anyways. In any case, the two variables are only exported to your namespace if \f(CW\*(C`startdir\*(C'\fR is true. .SH "OPTION VARIABLES AND OPTION TABLE" .IX Header "OPTION VARIABLES AND OPTION TABLE" Most long-running, computationally intensive scripts that spend a lot of time running other programs and read/write lots of (potentially big) files should be flexible enough for users to control a couple of basic aspects of their behaviour: the level of verbosity, whether sub-programs will actually be executed, whether pre-existing files should be clobbered, where to write temporary files, whether to clean up those temporary files, and so on. As it happens, \fIMNI::Spawn\fR provides a tailored solution to these problems, including global variables to guide the flow of control of your program and an option sub-table (for use with \fIGetopt::Tabular\fR) to allow the end user of your program to set those globals. These variables are only initialized and exported if the \&\f(CW\*(C`optvars\*(C'\fR option is true, and the option table is only initialized and exported if the \f(CW\*(C`opttable\*(C'\fR option is true. .SS "Option variables" .IX Subsection "Option variables" Most of the option variables initialized and exported by \fIMNI::Spawn\fR are boolean flags. Thus, each one has both a positive and negative option in the table meant for use with \fIGetopt::Tabular\fR. As explained in the \fIGetopt::Tabular\fR documentation, use of the positive option means the associated variable will be set to 1, and the negative option will set it to 0. The variables, and the command-line options (in positive/negative form for the boolean options) that can be used to control them, are: .ie n .IP "$Verbose (""\-verbose""/""\-quiet"") (initialized to: 1)" 4 .el .IP "\f(CW$Verbose\fR (\f(CW\-verbose\fR/\f(CW\-quiet\fR) (initialized to: 1)" 4 .IX Item "$Verbose (-verbose/-quiet) (initialized to: 1)" To be used as you see fit, but keep in mind that it is surreptitiously used by other modules (\fIMNI::Spawn\fR in particular\-\-\-see the \f(CW\*(C`verbose\*(C'\fR option in its documentation). I use it to control printing out useful information to the user, echoing all executed command lines, and controlling the verbosity of sub-programs (these last two with the help of the \fIMNI::Spawn\fR module). .ie n .IP "$Execute (""\-execute""/""\-noexecute"") (initialized to: 1)" 4 .el .IP "\f(CW$Execute\fR (\f(CW\-execute\fR/\f(CW\-noexecute\fR) (initialized to: 1)" 4 .IX Item "$Execute (-execute/-noexecute) (initialized to: 1)" Again to be used as you see fit, but also used by other modules (see \&\f(CW\*(C`execute\*(C'\fR in \fIMNI::Spawn\fR). I use it to control both the execution of sub-programs (with \fIMNI::Spawn\fR) and any operations that might affect the filesystem\-\-\-e.g. I only create directories or files if \f(CW$Execute\fR is true. .ie n .IP "$Clobber (""\-clobber""/""\-noclobber"") (initialized to: 0)" 4 .el .IP "\f(CW$Clobber\fR (\f(CW\-clobber\fR/\f(CW\-noclobber\fR) (initialized to: 0)" 4 .IX Item "$Clobber (-clobber/-noclobber) (initialized to: 0)" Use it to decide whether or not to overwrite existing files. Generally, my approach is that if \f(CW$Clobber\fR is true, I will silently overwrite existing files (which is what Unix tends to do for you anyways); if it is false, a pre-existing file is either a fatal error or is used instead of being re-created (depending on the context). \f(CW$Clobber\fR should also be propagated to the command lines of sub-programs that support such an option using \fIMNI::Spawn\fR's default arguments feature. .ie n .IP "$Debug (""\-debug""/""\-nodebug"") (initialized to: 0)" 4 .el .IP "\f(CW$Debug\fR (\f(CW\-debug\fR/\f(CW\-nodebug\fR) (initialized to: 0)" 4 .IX Item "$Debug (-debug/-nodebug) (initialized to: 0)" Controls whether you should print debugging information. The quantity and nature of this information is entirely up to you; \f(CW$Debug\fR should also be propagated to sub-programs that support it. .ie n .IP "$TmpDir (""\-tmpdir"")" 4 .el .IP "\f(CW$TmpDir\fR (\f(CW\-tmpdir\fR)" 4 .IX Item "$TmpDir (-tmpdir)" Specifies where to write temporary files; this is initialized to a unique directory constructed from \f(CW$ProgramName\fR and the process id (\f(CW$$\fR). This (hopefully) unique name is appended to \&\f(CW$ENV{\*(AqTMPDIR\*(Aq}\fR to make the complete directory. If the \s-1TMPDIR\s0 environment variable doesn't exist, then the following directories are checked, and the first found is used: \f(CW\*(Aq/usr/tmp\*(Aq\fR, \f(CW\*(Aq/var/tmp\*(Aq\fR, and \f(CW\*(Aq/tmp\*(Aq\fR. If \f(CW$ENV{\*(AqTMPDIR\*(Aq}\fR specifies a relative path, \f(CW$TmpDir\fR is made into an absolute path by prepending the current directory (from \f(CW$StartDir\fR\-\-\-this is the \*(L"certain obscure circumstance\*(R" where \fIMNI::Startup\fR ignores the \f(CW\*(C`startdir\*(C'\fR option and calls \f(CW\*(C`Cwd::getcwd\*(C'\fR anyways). .Sp If this directory is found to exist already, the module \f(CW\*(C`croak\*(C'\fRs. (This shouldn't happen, but it's conceivably possible, and it's not necessarily a bug in \fIMNI::Startup\fR. For instance, some previous run of your program might not have properly cleaned up after itself, or there might be another program with the same name and temporary directory naming scheme that didn't clean up after itself. Both of these, of course, assume that the previous run of the ill-behaved progam just happened to have the same process \s-1ID\s0 as the current run of your program\-\-\-hence, the small chance of this happening.) .Sp Note that the directory is \fInot\fR created, because the user might override it with the \f(CW\*(C`\-tmpdir\*(C'\fR command-line option. See \&\f(CW\*(C`MNI::FileUtilities::check_output_dirs\*(C'\fR for a safe and convenient way to create output directories such as \f(CW$TmpDir\fR. .Sp On shutdown, \fIMNI::Startup\fR will clean up this temporary directory for you by running \f(CW\*(C`rm \-rf\*(C'\fR on it. See \*(L"\s-1CLEANUP\s0\*(R" for details. .ie n .IP "$KeepTmp (""\-keeptmp""/""\-cleanup"") (initialized to: 0)" 4 .el .IP "\f(CW$KeepTmp\fR (\f(CW\-keeptmp\fR/\f(CW\-cleanup\fR) (initialized to: 0)" 4 .IX Item "$KeepTmp (-keeptmp/-cleanup) (initialized to: 0)" Can be used to disable cleaning up temporary files. This, along with several other conditions, is used by \fIMNI::Startup\fR on program shutdown to determine whether or not to cleanup \f(CW$TmpDir\fR. You might also use it in your program if you normally delete some temporary files along the way; if the user puts \f(CW\*(C`\-keeptmp\*(C'\fR on the command line (thus setting \&\f(CW$KeepTmp\fR true), you could respect this by not deleting anything so that all temporary files are preserved at the end of your program's run. .SS "Option table" .IX Subsection "Option table" \&\fIGetopt::Tabular\fR is a module for table-driven command line parsing; to make the global variables just described easily customizable by the end user, \fIMNI::Startup\fR provides a snippet of an option table in \&\f(CW@DefaultArgs\fR that you include in your main table for \&\fIGetopt::Tabular\fR. For example: .PP .Vb 7 \& use Getopt::Tabular; \& use MNI::Startup qw(optvars opttable); # redundant, but what the heck \& ... \& my @opt_table = \& (@DefaultArgs, # from MNI::Startup \& # rest of option table \& ); .Ve .PP This provides five boolean options (\f(CW\*(C`\-verbose\*(C'\fR, \f(CW\*(C`\-execute\*(C'\fR, \f(CW\*(C`\-clobber\*(C'\fR, \&\f(CW\*(C`\-debug\*(C'\fR, and \f(CW\*(C`\-keeptmp\*(C'\fR) along with one string option (\f(CW\*(C`\-tmpdir\*(C'\fR) corresponding to the six variables described above. .SH "RUNNING TIME" .IX Header "RUNNING TIME" \&\fIMNI::Spawn\fR can keep track of the \s-1CPU\s0 time used by your program and any child processes, and by the system on behalf of them. If the \f(CW\*(C`cputimes\*(C'\fR option is true, it will do just this and print out the \s-1CPU\s0 times used on program shutdown\-\-\-but only if the \f(CW$Verbose\fR global is also true and the program is exiting successfully (i.e. with a zero exit status). .SH "SIGNAL HANDLING" .IX Header "SIGNAL HANDLING" Finally, \fIMNI::Spawn\fR can install a signal handler for the most commonly encountered signals. This handler prints a message describing the signal we were hit by, cleans up (see \*(L"\s-1CLEANUP\s0\*(R" below), uninstalls itself, and then re-sends the same signal to the current process (i.e., your program). The effect of this is that the signal will \fInot\fR be caught this time, so your program will terminate abnormally just as though \fIMNI::Startup\fR's signal handler had never been there. The main advantage of this is that whichever program ran your program can examine its termination status and determine that it was indeed killed by a signal, rather than by \f(CW\*(C`exit\*(C'\fRing normally. .PP The signals handled fall into three groups: those you might normally expect to encounter (\s-1HUP\s0, \s-1INT\s0, \s-1QUIT\s0, \s-1PIPE\s0 and \s-1TERM\s0); those that indicate a serious problem with your script or the Perl interpreter running it (\s-1ILL\s0, \s-1TRAP\s0, \s-1ABRT\s0, \s-1IOT\s0, \s-1BUS\s0, \s-1EMT\s0, \s-1FPE\s0, \s-1SEGV\s0, and \s-1SYS\s0); and user-defined signals (\s-1USR1\s0 and \s-1USR2\s0). Note that not all of these signals are valid on a given platform, so \fIMNI::Startup\fR only installs handlers for the subset of these signals that Perl knows about. (With versions of Perl previous to 5.004, this information is not available, so \fIMNI::Startup\fR in that case installs handlers for the five \*(L"expected\*(R" signals only.) Currently, no distinction is made between the various groups of signals. .PP The \fIsigtrap\fR module provided with Perl 5.004 provides a more flexible approach to signal handling, but doesn't provide a signal handler to clean up your temporary directory. If you wish to use \fIMNI::Spawn\fR's signal handler with \fIsigtrap\fR's more flexible interface, just specify \&\f(CW\*(C`\e&MNI::Startup::catch_signal\*(C'\fR as your signal handler to \fIsigtrap\fR. Be sure that you also include \f(CW\*(C`nosig\*(C'\fR in \fIMNI::Startup\fR's import list, to disable its signal handling. (The version of \fIsigtrap\fR distributed with Perl 5.003 and earlier isn't nearly as flexible, so there's not much advantage in using \fIsigtrap\fR over \fIMNI::Startup\fR's signal handling unless you're running Perl 5.004 or later.) .SH "CLEANUP" .IX Header "CLEANUP" From the kernel's point-of-view, there are only two ways in which a process terminates: normally and abnormally. Programmers generally further distinguish between two kinds of normal termination, namely success and failure. In Perl, success is usually indicated by calling \&\f(CW\*(C`exit\*(C'\fR or by running off the end of the main program; failure is indicated by calling \f(CW\*(C`exit\*(C'\fR with a non-zero argument or \f(CW\*(C`die\*(C'\fR outside of any \f(CW\*(C`eval\*(C'\fR (an uncaught exception). Abnormal termination is what happens when we are hit by a signal, whether it's caused internally (e.g. a segmentation violation or floating-point exception) or externally (such as the user hitting Ctrl-C or another process sending the \f(CW\*(C`TERM\*(C'\fR signal). .PP Regardless of how your program terminates, \fIMNI::Startup\fR steps in to perform some cleaning up. In particular, it attempts to run \f(CW\*(C`rm \-rf\*(C'\fR on the temporary directory originally named by \f(CW$TmpDir\fR, but only if the \f(CW\*(C`cleanup\*(C'\fR option is true, the \f(CW$KeepTmp\fR global is false, and the temporary directory actually exists. Note that if you change \f(CW$TmpDir\fR (or if the end-user changes it with the \f(CW\*(C`\-tmpdir\*(C'\fR command-line option), then \fIMNI::Startup\fR will \fInot\fR clean up the new value of \f(CW$TmpDir\fR. (However, if you use the original value of \f(CW$TmpDir\fR for some files and then change its value and write new stuff in the new directory, then the original directory will be cleaned up\-\-\-just not the new one.) The rationale for this behaviour is that if the user (or the programmer) goes to the trouble of specifying a custom temporary directory, they probably want the files in it to last longer than your program's current execution. .SH "SUBROUTINES" .IX Header "SUBROUTINES" In addition to the startup/shutdown services described above, \&\fIMNI::Startup\fR also provides a couple of subroutines that are handy in many applications. These subroutines will be exported into your program's namespace if the \f(CW\*(C`subs\*(C'\fR option is true (as always, the default); if you instead supply \f(CW\*(C`nosubs\*(C'\fR in \fIMNI::Startup\fR's import list, they will of course still be available as \&\f(CW\*(C`MNI::Startup::self_announce\*(C'\fR and \f(CW\*(C`MNI::Startup::backgroundify\*(C'\fR. .IP "self_announce ([\s-1LOG\s0 [, \s-1PROGRAM\s0 [, \s-1ARGS\s0 [, \s-1FORCE\s0]]]])" 4 .IX Item "self_announce ([LOG [, PROGRAM [, ARGS [, FORCE]]]])" Conditionally prints a brief description of the program's execution environment: user, host, start directory, date, time, progam name, and program arguments. \s-1LOG\s0, if supplied, should be a filehandle reference (i.e., either a \s-1GLOB\s0 ref, an \f(CW\*(C`IO::Handle\*(C'\fR (or descendants) object, or a \&\f(CW\*(C`FileHandle\*(C'\fR object); it defaults to \f(CW\*(C`\e*STDOUT\*(C'\fR. \s-1PROGRAM\s0 should be the program name; it defaults to \f(CW$0\fR. \s-1ARGS\s0 should be a reference to the program's list of arguments; it defaults to \f(CW\*(C`\e@ARGV\*(C'\fR. (Thus, to ensure that \f(CW\*(C`self_announce\*(C'\fR prints an accurate record, you should never fiddle with \f(CW$0\fR or \f(CW@ARGV\fR in your program\-\-\-the former is made unnecessary by \&\fIMNI::Startup\fR's creation and export of \f(CW$ProgramName\fR, and the latter can be avoided without much trouble. The three-argument form of \&\f(CW\*(C`Getopt::Tabular::GetOptions\*(C'\fR, in particular, is designed to help you avoid clobbering \f(CW@ARGV\fR.) .Sp In general, you should put a call to \f(CW\*(C`self_announce\*(C'\fR somewhere in your program after all arguments have been validated, so you know that you're not going to crash immediately. If your program calls \f(CW\*(C`backgroundify\*(C'\fR, it's not necessary to also call \f(CW\*(C`self_announce\*(C'\fR in the same run, as \&\f(CW\*(C`backgroundify\*(C'\fR calls \f(CW\*(C`self_announce\*(C'\fR. Thus, in programs that put themselves into the background, you might see code like this: .Sp .Vb 1 \& $background ? backgroundify ($logfile) : self_announce; .Ve .Sp It shouldn't be necessary to put conditions on the call to \&\f(CW\*(C`self_announce\*(C'\fR (as was the case in versions of the \s-1MNI\s0 Perl Library up to 0.04). That's because there are (currently) two conditions that will cause \f(CW\*(C`self_announce\*(C'\fR to suppress its announcement for you. (You can always override this and force it to print its message by supplying a true value for \s-1FORCE\s0.) .Sp First, if \s-1LOG\s0 is a tty, \f(CW\*(C`self_announce\*(C'\fR will return without doing anything. That is, your program's output must be redirected to a file or pipe for the announcement to be made. This prevents pointlessly cluttering the display in an interactive run, but gives the user a record of exactly what command he ran to generate a particular log file (and the associated results). (The assumption here is that if a program's output is important enough to log, it's important to know the exact command executed. If the user didn't bother to log the output, he probably just ran the program from a shell, and can get back the command used anyways.) .Sp Second, if the environment variable \f(CW\*(C`suppress_announce\*(C'\fR is set to a true value, no announcement will be printed. This variable is normally set by the \fIMNI::Spawn\fR module; when \f(CW\*(C`Spawn\*(C'\fR considers it unnecessary for its child program (the program that eventually calls \&\f(CW\*(C`self_announce\*(C'\fR) to print out its arguments, then it will set this environment variable. The assumption here is that if \f(CW\*(C`Spawn\*(C'\fR already printed out the program name and arguments, and the program's output is not being redirected elsewhere, then it's not necessary for the child to replicate this information. See MNI::Spawn for full details. If \&\f(CW\*(C`self_announce\*(C'\fR does not find \f(CW\*(C`suppress_announce\*(C'\fR in its environment, then it is naturally treated as false. If it is found, it is deleted, so as not to affect other programs that might be called by your program. (Of course, if you use \fIMNI::Spawn\fR, then \f(CW\*(C`suppress_announce\*(C'\fR will be set all over again. It's only if you don't use \fIMNI::Spawn\fR to run your child programs that this matters.) .Sp Again, you can override the \*(L"is it a tty?\*(R" or "is \f(CW\*(C`suppress_announce\*(C'\fR set?" shenanigans by simply setting \s-1FORCE\s0 to true. .IP "backgroundify (\s-1LOG\s0 [, \s-1PROGRAM\s0 [, \s-1ARGS\s0]])" 4 .IX Item "backgroundify (LOG [, PROGRAM [, ARGS]])" Redirects \f(CW\*(C`STDOUT\*(C'\fR and \f(CW\*(C`STDERR\*(C'\fR to a log file and detaches to the background by forking off a child process. \s-1LOG\s0 must be either a filehandle (represented by a glob reference, or an \fIIO::Handle\fR (or descendents) object) or a filename; if the former, it is assumed that the file was opened for writing, and \f(CW\*(C`STDOUT\*(C'\fR and \f(CW\*(C`STDERR\*(C'\fR are redirected to that file. If \s-1LOG\s0 is not a reference, it is assumed to be a filename to be opened for output. You can supply a filename in the form of the second argument to \f(CW\*(C`open\*(C'\fR, i.e. with \f(CW\*(Aq>\*(Aq\fR or \&\f(CW\*(Aq>>\*(Aq\fR already prepended. If you just supply a bare filename, \&\f(CW\*(C`backgroundify\*(C'\fR will either clobber or append, depending on the value of the \f(CW$Clobber\fR global. \f(CW\*(C`backgroundify\*(C'\fR will then redirect \&\f(CW\*(C`STDOUT\*(C'\fR and \f(CW\*(C`STDERR\*(C'\fR both to this file. \s-1PROGRAM\s0 and \s-1ARGS\s0 are the same as for \f(CW\*(C`self_annouce\*(C'\fR; in fact, they are passed to \&\f(CW\*(C`self_announce\*(C'\fR after redirecting \f(CW\*(C`STDOUT\*(C'\fR and \f(CW\*(C`STDERR\*(C'\fR so that your program will describe its execution in its own log file. (Thus, it's never necessary to call both \f(CW\*(C`self_announce\*(C'\fR and \f(CW\*(C`backgroundify\*(C'\fR in the same run of a program.) .Sp After redirecting, \f(CW\*(C`backgroundify\*(C'\fR unbuffers both \f(CW\*(C`STDOUT\*(C'\fR and \&\f(CW\*(C`STDERR\*(C'\fR (so that messages to both streams will wind up in the same order as they are output by your program, and also to avoid problems with unflushed buffers before forking) and \f(CW\*(C`fork\*(C'\fRs. If the \f(CW\*(C`fork\*(C'\fR fails, the parent \f(CW\*(C`die\*(C'\fRs; otherwise, the parent \f(CW\*(C`exit\*(C'\fRs and the child returns 1. .Sp Be careful about calling \f(CW\*(C`backgroundify\*(C'\fR if you have any \f(CW\*(C`END\*(C'\fR blocks in your program: the \f(CW\*(C`END\*(C'\fR block will run in both the parent and the child, and it will run in the parent concurrently with \f(CW\*(C`backgroundify\*(C'\fR returning to your program as the child process. This would be a bad thing if, say, the \f(CW\*(C`END\*(C'\fR block run by the parent cleans up a temporary directory used by the child. \f(CW\*(C`backgroundify\*(C'\fR takes measures to ensure that this doesn't happen with the \f(CW\*(C`END\*(C'\fR block supplied by \&\fIMNI::Startup\fR and used for cleanup, but for you're on your own for any other \f(CW\*(C`END\*(C'\fR blocks in your program (or any in other modules that you might use). .Sp Note that \f(CW\*(C`backgroundify\*(C'\fR is \fInot\fR sufficient for forking off a daemon process. This requires a slightly different flavour of wizardry, which is well outside the scope of this module and this man page. Anyways, glorified shell scripts probably shouldn't be made into daemons. .SH "AUTHOR" .IX Header "AUTHOR" Greg Ward, . .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright (c) 1997 by Gregory P. Ward, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. .PP This file is part of the \s-1MNI\s0 Perl Library. It is free software, and may be distributed under the same terms as Perl itself.