]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/sendmail/contrib/smcontrol.pl
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / sendmail / contrib / smcontrol.pl
1 #!/usr/bin/perl -w
2
3 # $Id: smcontrol.pl,v 8.8 2008/07/21 21:31:43 ca Exp $
4
5 use strict;
6 use Getopt::Std;
7 use FileHandle;
8 use Socket;
9
10 my $sendmailDaemon = "/usr/sbin/sendmail -q30m -bd";
11
12 ##########################################################################
13 #
14 #  &get_controlname -- read ControlSocketName option from sendmail.cf
15 #
16 #       Parameters:
17 #               none.
18 #
19 #       Returns:
20 #               control socket filename, undef if not found
21 #
22
23 sub get_controlname
24 {
25         my $cn = undef;
26         my $qd = undef;
27  
28         open(CF, "</etc/mail/sendmail.cf") or return $cn;
29         while (<CF>)
30         {
31                 chomp;
32                 if (/^O ControlSocketName\s*=\s*([^#]+)$/o)
33                 {
34                         $cn = $1;
35                 }
36                 if (/^O QueueDirectory\s*=\s*([^#]+)$/o)
37                 {
38                         $qd = $1;
39                 }
40                 if (/^OQ([^#]+)$/o)
41                 {
42                         $qd = $1;
43                 }
44         }
45         close(CF);
46         if (not defined $cn)
47         {
48                 return undef;
49         }
50         if ($cn !~ /^\//o)
51         {
52                 return undef if (not defined $qd);
53                 
54                 $cn = $qd . "/" . $cn;
55         }
56         return $cn;
57 }
58
59 ##########################################################################
60 #
61 #  &do_command -- send command to sendmail daemon view control socket
62 #
63 #       Parameters:
64 #               controlsocket -- filename for socket
65 #               command -- command to send
66 #
67 #       Returns:
68 #               reply from sendmail daemon
69 #
70
71 sub do_command
72 {
73         my $controlsocket = shift;
74         my $command = shift;
75         my $proto = getprotobyname('ip');
76         my @reply;
77         my $i;
78
79         socket(SOCK, PF_UNIX, SOCK_STREAM, $proto) or return undef;
80
81         for ($i = 0; $i < 4; $i++)
82         {
83                 if (!connect(SOCK, sockaddr_un($controlsocket)))
84                 {
85                         if ($i == 3)
86                         {
87                                 close(SOCK);
88                                 return undef;
89                         }
90                         sleep 1;
91                         next;
92                 }
93                 last;
94         }
95         autoflush SOCK 1;
96         print SOCK "$command\n";
97         @reply = <SOCK>;
98         close(SOCK);
99         return join '', @reply;
100 }
101
102 ##########################################################################
103 #
104 #  &sendmail_running -- check if sendmail is running via SMTP
105 #
106 #       Parameters:
107 #               none
108 #
109 #       Returns:
110 #               1 if running, undef otherwise
111 #
112
113 sub sendmail_running
114 {
115         my $port = getservbyname("smtp", "tcp") || 25;
116         my $proto = getprotobyname("tcp");
117         my $iaddr = inet_aton("localhost");
118         my $paddr = sockaddr_in($port, $iaddr);
119
120         socket(SOCK, PF_INET, SOCK_STREAM, $proto) or return undef;
121         if (!connect(SOCK, $paddr))
122         {
123                 close(SOCK);
124                 return undef;
125         }
126         autoflush SOCK 1;
127         while (<SOCK>)
128         {
129                 if (/^(\d{3})([ -])/)
130                 {
131                         if ($1 != 220)
132                         {
133                                 close(SOCK);
134                                 return undef;
135                         }
136                 }
137                 else
138                 {
139                         close(SOCK);
140                         return undef;
141                 }
142                 last if ($2 eq " ");
143         }
144         print SOCK "QUIT\n";
145         while (<SOCK>)
146         {
147                 last if (/^\d{3} /);
148         }
149         close(SOCK);
150         return 1;
151 }
152
153 ##########################################################################
154 #
155 #  &munge_status -- turn machine readable status into human readable text
156 #
157 #       Parameters:
158 #               raw -- raw results from sendmail daemon STATUS query
159 #
160 #       Returns:
161 #               human readable text
162 #
163
164 sub munge_status
165 {
166         my $raw = shift;
167         my $cooked = "";
168         my $daemonStatus = "";
169
170         if ($raw =~ /^(\d+)\/(\d+)\/(\d+)\/(\d+)/mg)
171         {
172                 $cooked .= "Current number of children: $1";
173                 if ($2 > 0)
174                 {
175                         $cooked .= " (maximum $2)";
176                 }
177                 $cooked .= "\n";
178                 $cooked .= "QueueDir free disk space (in blocks): $3\n";
179                 $cooked .= "Load average: $4\n";
180         }
181         while ($raw =~ /^(\d+) (.*)$/mg)
182         {
183                 if (not $daemonStatus)
184                 {
185                         $daemonStatus = "(process $1) " . ucfirst($2) . "\n";
186                 }
187                 else
188                 {
189                         $cooked .= "Child Process $1 Status: $2\n";
190                 }
191         }
192         return ($daemonStatus, $cooked);
193 }
194
195 ##########################################################################
196 #
197 #  &start_daemon -- fork off a sendmail daemon
198 #
199 #       Parameters:
200 #               control -- control socket name
201 #
202 #       Returns:
203 #               Error message or "OK" if successful
204 #
205
206 sub start_daemon
207 {
208         my $control = shift;
209         my $pid;
210
211         if ($pid = fork)
212         {
213                 my $exitstat;
214
215                 waitpid $pid, 0 or return "Could not get status of created process: $!\n";
216                 $exitstat = $? / 256;
217                 if ($exitstat != 0)
218                 {
219                         return "sendmail daemon startup exited with exit value $exitstat";
220                 }
221         }
222         elsif (defined $pid)
223         {
224                 exec($sendmailDaemon);
225                 die "Unable to start sendmail daemon: $!.\n";
226         }
227         else
228         {
229                 return "Could not create new process: $!\n";
230         }
231         return "OK\n";
232 }
233
234 ##########################################################################
235 #
236 #  &stop_daemon -- stop the sendmail daemon using control socket
237 #
238 #       Parameters:
239 #               control -- control socket name
240 #
241 #       Returns:
242 #               Error message or status message
243 #
244
245 sub stop_daemon
246 {
247         my $control = shift;
248         my $status;
249
250         if (not defined $control)
251         {
252                 return "The control socket is not configured so the daemon can not be stopped.\n";
253         }
254         return &do_command($control, "SHUTDOWN");
255 }
256
257 ##########################################################################
258 #
259 #  &restart_daemon -- restart the sendmail daemon using control socket
260 #
261 #       Parameters:
262 #               control -- control socket name
263 #
264 #       Returns:
265 #               Error message or status message
266 #
267
268 sub restart_daemon
269 {
270         my $control = shift;
271         my $status;
272
273         if (not defined $control)
274         {
275                 return "The control socket is not configured so the daemon can not be restarted.";
276         }
277         return &do_command($control, "RESTART");
278 }
279
280 ##########################################################################
281 #
282 #  &memdump -- get memdump from the daemon using the control socket
283 #
284 #       Parameters:
285 #               control -- control socket name
286 #
287 #       Returns:
288 #               Error message or status message
289 #
290
291 sub memdump
292 {
293         my $control = shift;
294         my $status;
295
296         if (not defined $control)
297         {
298                 return "The control socket is not configured so the daemon can not be queried for memdump.";
299         }
300         return &do_command($control, "MEMDUMP");
301 }
302
303 ##########################################################################
304 #
305 #  &help -- get help from the daemon using the control socket
306 #
307 #       Parameters:
308 #               control -- control socket name
309 #
310 #       Returns:
311 #               Error message or status message
312 #
313
314 sub help
315 {
316         my $control = shift;
317         my $status;
318
319         if (not defined $control)
320         {
321                 return "The control socket is not configured so the daemon can not be queried for help.";
322         }
323         return &do_command($control, "HELP");
324 }
325
326 my $status = undef;
327 my $daemonStatus = undef;
328 my $opts = {};
329
330 getopts('f:', $opts) || die "Usage: $0 [-f /path/to/control/socket] command\n";
331
332 my $control = $opts->{f} || &get_controlname;
333 my $command = shift;
334
335 if (not defined $control)
336 {
337         die "No control socket available.\n";
338 }
339 if (not defined $command)
340 {
341         die "Usage: $0 [-f /path/to/control/socket] command\n";
342 }
343 if ($command eq "status")
344 {
345         $status = &do_command($control, "STATUS");
346         if (not defined $status)
347         {
348                 # Not responding on control channel, query via SMTP
349                 if (&sendmail_running)
350                 {
351                         $daemonStatus = "Sendmail is running but not answering status queries.";
352                 }
353                 else
354                 {
355                         $daemonStatus = "Sendmail does not appear to be running.";
356                 }
357         }
358         else
359         {
360                 # Munge control channel output
361                 ($daemonStatus, $status) = &munge_status($status);
362         }
363 }
364 elsif (lc($command) eq "shutdown")
365 {
366         $status = &stop_daemon($control);
367 }
368 elsif (lc($command) eq "restart")
369 {
370         $status = &restart_daemon($control);
371 }
372 elsif (lc($command) eq "start")
373 {
374         $status = &start_daemon($control);
375 }
376 elsif (lc($command) eq "memdump")
377 {
378         $status = &memdump($control);
379 }
380 elsif (lc($command) eq "help")
381 {
382         $status = &help($control);
383 }
384 elsif (lc($command) eq "mstat")
385 {
386         $status = &do_command($control, "mstat");
387         if (not defined $status)
388         {
389                 # Not responding on control channel, query via SMTP
390                 if (&sendmail_running)
391                 {
392                         $daemonStatus = "Sendmail is running but not answering status queries.";
393                 }
394                 else
395                 {
396                         $daemonStatus = "Sendmail does not appear to be running.";
397                 }
398         }
399 }
400 else
401 {
402         die "Unrecognized command $command\n";
403 }
404 if (defined $daemonStatus)
405 {
406         print "Daemon Status: $daemonStatus\n";
407 }
408 if (defined $status)
409 {
410         print "$status\n";
411 }
412 else
413 {
414         die "No response\n";
415 }