1 /* $OpenBSD: smtpctl.c,v 1.24 2009/04/23 08:28:45 jacekm Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> 5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 6 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 7 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/types.h> 23 #include <sys/socket.h> 24 #include <sys/queue.h> 25 #include <sys/tree.h> 26 #include <sys/un.h> 27 28 #include <net/if.h> 29 #include <net/if_media.h> 30 #include <net/if_types.h> 31 #include <netinet/in.h> 32 #include <arpa/inet.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <event.h> 41 42 #include "smtpd.h" 43 #include "parser.h" 44 45 __dead void usage(void); 46 int show_command_output(struct imsg*); 47 int show_stats_output(struct imsg *); 48 49 /* 50 struct imsgname { 51 int type; 52 char *name; 53 void (*func)(struct imsg *); 54 }; 55 56 struct imsgname imsgs[] = { 57 { IMSG_CTL_SHUTDOWN, "stop", NULL }, 58 { IMSG_CONF_RELOAD, "reload", NULL }, 59 { 0, NULL, NULL } 60 }; 61 struct imsgname imsgunknown = { 62 -1, "<unknown>", NULL 63 }; 64 */ 65 66 int proctype; 67 struct imsgbuf *ibuf; 68 69 int sendmail = 0; 70 extern char *__progname; 71 72 __dead void 73 usage(void) 74 { 75 extern char *__progname; 76 77 if (sendmail) 78 fprintf(stderr, "usage: %s [-tv] [-f from] [-F name] to ..\n", 79 __progname); 80 else 81 fprintf(stderr, "usage: %s command [argument ...]\n", __progname); 82 exit(1); 83 } 84 85 /* dummy function so that smtpctl does not need libevent */ 86 void 87 imsg_event_add(struct imsgbuf *i) 88 { 89 /* nothing */ 90 } 91 92 int 93 main(int argc, char *argv[]) 94 { 95 struct sockaddr_un sun; 96 struct parse_result *res = NULL; 97 struct imsg imsg; 98 int ctl_sock; 99 int done = 0; 100 int n; 101 102 /* parse options */ 103 if (strcmp(__progname, "sendmail") == 0 || strcmp(__progname, "send-mail") == 0) 104 sendmail = 1; 105 else { 106 /* check for root privileges */ 107 if (geteuid()) 108 errx(1, "need root privileges"); 109 110 if ((res = parse(argc - 1, argv + 1)) == NULL) 111 exit(1); 112 113 /* handle "disconnected" commands */ 114 switch (res->action) { 115 case SHOW_QUEUE: 116 show_queue(PATH_QUEUE, 0); 117 break; 118 case SHOW_RUNQUEUE: 119 show_queue(PATH_RUNQUEUE, 0); 120 break; 121 default: 122 goto connected; 123 } 124 return 0; 125 } 126 127 connected: 128 /* connect to relayd control socket */ 129 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 130 err(1, "socket"); 131 132 bzero(&sun, sizeof(sun)); 133 sun.sun_family = AF_UNIX; 134 strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path)); 135 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 136 if (sendmail) 137 return enqueue_offline(argc, argv); 138 err(1, "connect: %s", SMTPD_SOCKET); 139 } 140 141 if ((ibuf = calloc(1, sizeof(struct imsgbuf))) == NULL) 142 err(1, NULL); 143 imsg_init(ibuf, ctl_sock, NULL); 144 145 if (sendmail) 146 return enqueue(argc, argv); 147 148 /* process user request */ 149 switch (res->action) { 150 case NONE: 151 usage(); 152 /* not reached */ 153 case SHUTDOWN: 154 imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0); 155 break; 156 case RELOAD: 157 imsg_compose(ibuf, IMSG_CONF_RELOAD, 0, 0, -1, NULL, 0); 158 break; 159 case PAUSE_MDA: 160 imsg_compose(ibuf, IMSG_MDA_PAUSE, 0, 0, -1, NULL, 0); 161 break; 162 case PAUSE_MTA: 163 imsg_compose(ibuf, IMSG_MTA_PAUSE, 0, 0, -1, NULL, 0); 164 break; 165 case PAUSE_SMTP: 166 imsg_compose(ibuf, IMSG_SMTP_PAUSE, 0, 0, -1, NULL, 0); 167 break; 168 case RESUME_MDA: 169 imsg_compose(ibuf, IMSG_MDA_RESUME, 0, 0, -1, NULL, 0); 170 break; 171 case RESUME_MTA: 172 imsg_compose(ibuf, IMSG_MDA_RESUME, 0, 0, -1, NULL, 0); 173 break; 174 case RESUME_SMTP: 175 imsg_compose(ibuf, IMSG_SMTP_RESUME, 0, 0, -1, NULL, 0); 176 break; 177 case SHOW_STATS: 178 imsg_compose(ibuf, IMSG_STATS, 0, 0, -1, NULL, 0); 179 break; 180 case SCHEDULE: { 181 struct sched s; 182 183 s.fd = -1; 184 bzero(s.mid, sizeof (s.mid)); 185 strlcpy(s.mid, res->data, sizeof (s.mid)); 186 imsg_compose(ibuf, IMSG_RUNNER_SCHEDULE, 0, 0, -1, &s, sizeof (s)); 187 break; 188 } 189 case MONITOR: 190 /* XXX */ 191 break; 192 default: 193 err(1, "unknown request (%d)", res->action); 194 } 195 196 while (ibuf->w.queued) 197 if (msgbuf_write(&ibuf->w) < 0) 198 err(1, "write error"); 199 200 while (!done) { 201 if ((n = imsg_read(ibuf)) == -1) 202 errx(1, "imsg_read error"); 203 if (n == 0) 204 errx(1, "pipe closed"); 205 206 while (!done) { 207 if ((n = imsg_get(ibuf, &imsg)) == -1) 208 errx(1, "imsg_get error"); 209 if (n == 0) 210 break; 211 switch(res->action) { 212 case RELOAD: 213 case SHUTDOWN: 214 case SCHEDULE: 215 case PAUSE_MDA: 216 case PAUSE_MTA: 217 case PAUSE_SMTP: 218 case RESUME_MDA: 219 case RESUME_MTA: 220 case RESUME_SMTP: 221 done = show_command_output(&imsg); 222 break; 223 case SHOW_STATS: 224 done = show_stats_output(&imsg); 225 break; 226 case NONE: 227 break; 228 case MONITOR: 229 break; 230 default: 231 err(1, "unexpected reply (%d)", res->action); 232 } 233 /* insert imsg replies switch here */ 234 235 imsg_free(&imsg); 236 } 237 } 238 close(ctl_sock); 239 free(ibuf); 240 241 return (0); 242 } 243 244 int 245 show_command_output(struct imsg *imsg) 246 { 247 switch (imsg->hdr.type) { 248 case IMSG_CTL_OK: 249 printf("command succeeded\n"); 250 break; 251 case IMSG_CTL_FAIL: 252 printf("command failed\n"); 253 break; 254 default: 255 errx(1, "wrong message in summary: %u", imsg->hdr.type); 256 } 257 return (1); 258 } 259 260 int 261 show_stats_output(struct imsg *imsg) 262 { 263 static int left = 4; 264 static struct s_parent s_parent; 265 static struct s_queue s_queue; 266 static struct s_runner s_runner; 267 static struct s_session s_smtp; 268 269 switch (imsg->hdr.type) { 270 case IMSG_PARENT_STATS: 271 s_parent = *(struct s_parent *)imsg->data; 272 break; 273 case IMSG_QUEUE_STATS: 274 s_queue = *(struct s_queue *)imsg->data; 275 break; 276 case IMSG_RUNNER_STATS: 277 s_runner = *(struct s_runner *)imsg->data; 278 break; 279 case IMSG_SMTP_STATS: 280 s_smtp = *(struct s_session *)imsg->data; 281 break; 282 default: 283 errx(1, "show_stats_output: bad hdr type (%d)", imsg->hdr.type); 284 } 285 286 left--; 287 if (left > 0) 288 return (0); 289 290 printf("parent.uptime=%d\n", time(NULL) - s_parent.start); 291 292 printf("queue.inserts.local=%zd\n", s_queue.inserts_local); 293 printf("queue.inserts.remote=%zd\n", s_queue.inserts_remote); 294 295 printf("runner.active=%zd\n", s_runner.active); 296 297 printf("smtp.sessions=%zd\n", s_smtp.sessions); 298 printf("smtp.sessions.aborted=%zd\n", s_smtp.aborted); 299 printf("smtp.sessions.active=%zd\n", s_smtp.sessions_active); 300 printf("smtp.sessions.timeout=%zd\n", s_smtp.timeout); 301 printf("smtp.sessions.smtps=%zd\n", s_smtp.smtps); 302 printf("smtp.sessions.smtps.active=%zd\n", s_smtp.smtps_active); 303 printf("smtp.sessions.starttls=%zd\n", s_smtp.starttls); 304 printf("smtp.sessions.starttls.active=%zd\n", s_smtp.starttls_active); 305 306 return (1); 307 } 308