lists.def blocks http mboxes, unused otherwise
[mharc.git] / bin / logcmd
CommitLineData
2ea8f66b
IK
1#!/usr/bin/perl -w
2##------------------------------------------------------------------------##
3## File:
4## $Id: logcmd,v 1.1 2002/10/01 22:49:46 ehood Exp $
5## Author:
6## Earl Hood earl@earlhood.com
7## Description:
8## Program to log the output of a program.
9## POD at __END__.
10##------------------------------------------------------------------------##
11## Copyright (C) 2002 Earl Hood, earl@earlhood.com
12##
13## This program is free software; you can redistribute it and/or modify
14## it under the terms of the GNU General Public License as published by
15## the Free Software Foundation; either version 2 of the License, or
16## (at your option) any later version.
17##
18## This program is distributed in the hope that it will be useful,
19## but WITHOUT ANY WARRANTY; without even the implied warranty of
20## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21## GNU General Public License for more details.
22##
23## You should have received a copy of the GNU General Public License
24## along with this program; if not, write to the Free Software
25## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
26## 02111-1307, USA
27##------------------------------------------------------------------------##
28
29package Devtools::locmd;
30
31use strict;
32use Getopt::Long;
33use POSIX;
34
35my $our_name = 'logcmd';
36my @cmd = ( );
37my $cmd_name = "";
38my $time_fmt = '%Y-%m-%d %H:%M:%S';
39
40my $first_time = 1;
41my $log_file = '-';
42my $log_fh;
43my $child_pid;
44
45MAIN: {
46 my @mailto = ( );
47 my ($help, $man);
48
49 GetOptions(
50 'log=s' => \$log_file,
51 'mailto=s@' => \@mailto, # not implemented, yet
52 'time-fmt=s' => \$time_fmt,
53
54 'help' => \$help,
55 'man' => \$man,
56 ) || die qq/ERROR: Use -help for usage information\n/;
57 usage(1, 0) if ($help);
58 usage(2, 0) if ($man);
59
60 if (!@ARGV) {
61 die qq/ERROR: No command specified\n/;
62 }
63 ($cmd_name = $ARGV[0]) =~ s/.*\///;
64 @cmd = @ARGV;
65
66 $child_pid = open(CMD, "-|");
67 if ($child_pid) { # parent
68 {
69 local @SIG{'PIPE','TERM','INT'} = (sub {
70 log_line($our_name, $$, "Caught signal: SIG$_[0]\n");
71 }) x 3;
72
73 while (<CMD>) {
74 log_line($cmd_name, $child_pid, $_);
75 }
76 }
77 if (!close(CMD)) {
78 log_line($our_name, $$, qq/Non-zero exit for [$child_pid]: $?\n/);
79 }
80 log_close();
81
82 } else { # child
83 open(STDERR, '>&STDOUT');
84 exec(@cmd) || die qq/ERROR: Cannot exec "@cmd": $!\n/;
85 }
86
87} # End: MAIN
88
89##=========================================================================##
90
91sub format_time {
92 POSIX::strftime($time_fmt, localtime);
93}
94
95sub log_line {
96 my $label = shift;
97 my $pid = shift;
98 my $line = shift;
99 my $have_newline = $line =~ /\n\Z/;
100 my $eol = ($have_newline) ? "" : "\n";
101 if ($first_time) {
102 $first_time = 0;
103 log_open($log_file);
104 print $log_fh join('', '[',format_time(),']',' [',$$,'] ',$our_name,': ',
105 "Command: [$child_pid] @cmd\n");
106 }
107 print $log_fh join('', '[',format_time(),']',' [',$pid,'] ',$label,': ',
108 $line,$eol);
109}
110
111sub log_open {
112 my $file = shift;
113 if (!defined($file) || $file eq '-') {
114 $log_fh = \*STDOUT;
115 } else {
116 open(LOG, ">>$file") ||
117 die qq/ERROR: Unable to open "$file" for appending: $!\n/;
118 $log_fh = \*LOG;
119 }
120}
121
122sub log_close {
123 if (!$first_time) {
124 if ($log_fh != \*STDOUT) {
125 close($log_fh) ||
126 warn qq/Warning: Problem closing log file: $!\n/;
127 }
128 }
129}
130
131sub usage {
132 require Pod::Usage;
133 my $verbose = shift || 0;
134 my $exit_code = shift;
135
136 if ($verbose == 0) {
137 Pod::Usage::pod2usage(-verbose => $verbose);
138 } else {
139 my $pager = $ENV{'PAGER'} || 'more';
140 local(*PAGER);
141 my $fh = (-t STDOUT && open(PAGER, "|$pager")) ? \*PAGER : \*STDOUT;
142 Pod::Usage::pod2usage(-verbose => $verbose,
143 -output => $fh);
144 close(PAGER) if ($fh == \*PAGER);
145 }
146 defined($exit_code) && exit($exit_code);
147}
148
149##=========================================================================##
150__END__
151
152=head1 NAME
153
154logcmd - Log the output of a program.
155
156=head1 SYNOPSIS
157
158 logcmd [options] -- <command> [command-args ...]
159
160=head1 DESCRIPTION
161
162B<logcmd> logs the output of a program. This program is useful
163for logging program output for programs that do not have a built-in
164logging facility.
165
166Typical usage is to use B<logcmd> in crontabs to log any command
167output to a file instead of mail automatically being sent to the
168crontab owner.
169
170When invoking B<logcmd>, you B<must> use "C<-->" to separate the
171B<logcmd> and its options from the command you are invoking. For
172example,
173
174 logcmd -log out.log -- some-cmd arg1 arg2 arg3
175
176This tells B<logcmd> to run "C<some-cmd arg1 arg2 arg3>" and have
177any output goto C<out.log>.
178
179Each line logged will be preceded by a timestamp, the process ID, and
180the name of the command. Also, before the first log message for a command
181is printed, B<logcmd> will print out a line giving the full command-line
182of the command invoked. Example:
183
184 [2002-07-30 14:52:13] [25392] logcmd: Command: [25393] mhonarc -quiet -out out /var/archiver/mail
185 [2002-07-30 14:52:13] [25393] mhonarc:
186 [2002-07-30 14:52:13] [25393] mhonarc: Warning: Unrecognized character set: utf-8
187 [2002-07-30 14:52:13] [25393] mhonarc: Message-Id: <1022714308.2028.1.camel@xxx.xxx.xxx>
188 [2002-07-30 14:52:13] [25393] mhonarc: Message Number: 00367
189 [2002-07-30 14:52:13] [25393] mhonarc:
190 [2002-07-30 14:52:13] [25393] mhonarc: Warning: Unrecognized character set: utf-8
191 [2002-07-30 14:52:13] [25393] mhonarc: Message-Id: <1022716087.2028.3.camel@xxx.xxx.xxx>
192 [2002-07-30 14:52:13] [25393] mhonarc: Message Number: 00368
193 [2002-07-30 14:52:14] [25393] mhonarc:
194 [2002-07-30 14:52:14] [25393] mhonarc: Warning: Unrecognized character set: utf-8
195 [2002-07-30 14:52:14] [25393] mhonarc: Message-Id: <1024365245.9394.0.camel@xxx.xxx.xxx>
196 [2002-07-30 14:52:14] [25393] mhonarc: Message Number: 00398
197
198
199=head1 OPTIONS
200
201=over
202
203=item C<-help>
204
205Display SYNOPSIS and OPTIONS sections.
206
207=item C<-log> I<pathname>
208
209Command output will be logged to I<pathname>. If this option is
210not specified, then standard out is used.
211
212=item C<-man>
213
214Display manpage.
215
216=item C<-time-fmt> I<fmt>
217
218Date/time format to use when printing time before each logged line.
219The format string should be in C<strftime>(3) format. If this
220option is not specified, then "C<%Y-%m-%d %H:%M:%S>" is used.
221
222=item C<-->
223
224Terminate option processing for B<logcmd>. Any arguments after
225C<--> comprise the command that B<logcmd> should execute.
226
227=back
228
229=head1 EXAMPLES
230
231The following is an example crontab entries of a user that has
232any output of commands goto a personal log file:
233
234 0 2 * * * logcmd -log $HOME/log/cron.log -- $HOME/bin/clean_account
235 0 3 * * * logcmd -log $HOME/log/cron.log -- $HOME/bin/pack_mail
236
237=head1 LIMITATIONS
238
239=over
240
241=item *
242
243B<logcmd> performs no file locking on the log file specified. To help
244minimize that a single output line to the log does not get broken up
245due to multiple writers, the entire line is dumped in a single call
246to Perl's C<print> operator.
247
248=back
249
250=head1 DEPENDENCIES
251
252C<Getopt::Long>, C<POSIX>.
253
254=head1 AUTHOR
255
256Earl Hood, earl@earlhood.com
257
258This program comes with ABSOLUTELY NO WARRANTY and may be copied only
259under the terms of the GNU General Public License.
260
261=cut
262