3 # Utility for searching and displaying queue information.
4 # Written by Matt Hubbard 15 August 2002
6 # Copyright (c) The Exim Maintainers 2021 - 2023
7 # SPDX-License-Identifier: GPL-2.0-or-later
8 # See the file NOTICE for conditions of use and distribution.
10 # Except when they appear in comments, the following placeholders in this
11 # source are replaced when it is turned into a runnable script:
19 # Routine for extracting the UTC timestamp from message ID
20 # lifted from eximstat utility
25 BEGIN { pop @INC if $INC[-1] eq '.' };
30 # Have this variable point to your exim binary.
31 my $exim = 'BIN_DIRECTORY/exim';
38 (0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, # 0-9
39 0,10,11,12,13,14,15,16,17,18,19,20, # A-K
40 21,22,23,24,25,26,27,28,29,30,31,32, # L-W
41 33,34,35, 0, 0, 0, 0, 0, # X-Z
42 0,36,37,38,39,40,41,42,43,44,45,46, # a-k
43 47,48,49,50,51,52,53,54,55,56,57,58, # l-w
47 if ($^O eq 'darwin') { # aka MacOS X
53 if ($ARGV[0] eq '--version' || $ARGV[0] eq '-v') {
54 print basename($0) . ": $0\n",
55 "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n",
56 "perl(runtime): $]\n";
60 if (!getopts('hf:r:y:o:s:C:zxlibRcaG:E:',\%opt)) { &help; exit; }
61 if ($opt{h}) { &help; exit; }
62 if ($ARGV[0] || !($opt{f} || $opt{r} || $opt{s} || $opt{y} || $opt{o} || $opt{z} || $opt{x} || $opt{c}))
64 if ($opt{a}) { $eargs = '-bp'; }
65 if ($opt{C} && -e $opt{C} && -f $opt{C} && -R $opt{C}) { $eargs .= ' -C '.$opt{C}; }
66 if ($opt{G}) { $eargs .= ' -qG'.$opt{G}; }
67 if ($opt{E}) { $exim = $opt{E}; }
69 # Read message queue output into hash
71 # Identify which messages match selection criteria
73 # Print matching data according to display option.
80 Exim message queue display utility.
83 -C Specify which exim.conf to use.
84 -E Specify exim binary to use.
87 -f <regexp> Match sender address sender (field is "< >" wrapped)
88 -r <regexp> Match recipient address
89 -s <regexp> Match against the size field from long output
90 -y <seconds> Message younger than
91 -o <seconds> Message older than
92 -z Frozen messages only (exclude non-frozen)
93 -x Non-frozen messages only (exclude frozen)
94 -G <queuename> Match in given queue only
96 [ NB: for regexps, provided string sits in /<string>/ ]
99 -c Display match count
100 -l Long Format [Default]
104 -a All recipients (including delivered)
109 open(QUEUE,"$exim $eargs |") or die("Error opening pipe: $!\n");
113 #Should be 1st line of record, if not error.
114 if ($line =~ /^\s*(?<age>\w+)
115 \s+(?<size>(?:\d+(?:\.\d+)?[A-Z]?)?)
116 \s*(?<msgid>(?:\w{6}-\w{6}-\w{2}|\w{6}-\w{11}-\w{4})) # old, 2023 msgid formats
117 \s+(?<from><.*?>)/x) {
119 $id{$msg}{age} = $+{age};
120 $id{$msg}{size} = $+{size};
121 $id{$msg}{from} = $+{from};
122 $id{$msg}{birth} = &msg_utc($msg);
123 $id{$msg}{ages} = time - $id{$msg}{birth};
124 $id{$msg}{frozen} = ($line =~ /\*\*\* frozen \*\*\*$/) ? 1 : 0;
125 while(<QUEUE> =~ /\s+(.*?\@.*)$/) {
126 push(@{$id{$msg}{rcpt}},$1);
128 # Increment message counter.
131 print STDERR "Line mismatch: $line\n"; exit 1;
134 close(QUEUE) or die("Error closing pipe: $!\n");
138 foreach my $msg (keys(%id)) {
140 # Match sender address
141 next unless ($id{$msg}{from} =~ /$opt{f}/i);
144 # Match any recipient address
146 foreach my $rcpt (@{$id{$msg}{rcpt}}) {
147 $match++ if ($rcpt =~ /$opt{r}/i);
149 next unless ($match);
152 # Match against the size string.
153 next unless ($id{$msg}{size} =~ /$opt{s}/);
157 next unless ($id{$msg}{ages} < $opt{y});
161 next unless ($id{$msg}{ages} > $opt{o});
165 next unless ($id{$msg}{frozen});
169 next if ($id{$msg}{frozen});
171 # Here's what we do to select the record.
172 # Should only get this far if the message passed all of
175 # Increment match counter.
182 printf("%d matches out of %d messages\n",$mcount,$count);
185 foreach my $msg (sort { $opt{R} ? $id{$b}{birth} <=> $id{$a}{birth} : $id{$a}{birth} <=> $id{$b}{birth} } keys(%id) ) {
186 if (exists($id{$msg}{d})) {
192 printf("%s From: %s To: %s\n",$msg,$id{$msg}{from},join(';',@{$id{$msg}{rcpt}}))
194 # Otherwise Long format attempted duplication of original format.
195 printf("%3s %5s %s %s%s\n",$id{$msg}{age},$id{$msg}{size},$msg,$id{$msg}{from},$id{$msg}{frozen} ? " *** frozen ***" : "");
196 foreach my $rcpt (@{$id{$msg}{rcpt}}) {
197 printf(" %s\n",$rcpt);
206 foreach my $msg (keys(%id)) {
207 print "$id{$msg}{birth} $msg\tAge: $id{$msg}{age}\tSize: $id{$msg}{size}\tFrom: $id{$msg}{from}\tTo: " . join(" ",@{$id{$msg}{rcpt}}). "\n";
212 my $id = substr((pop @_), 0, 6);
214 my @c = split(//, $id);
215 while($#c >= 0) { $s = $s * $base + $tab62[ord(shift @c) - ord('0')] }