masqmail

annotate src/masqmail.c @ 421:f37384470855

Changed lockdir to /var/lock/masqmail; Create lockdir and piddir on startup. Moved the lockdir out of the spool dir. (When /var/lock is a ramdisk we do well to have the lock files there.) Added the new configure option --with-lockdir to change that location. Nontheless, if we run_as_user, then lock files are always stored in the spool dir directly. Instead of installing the lockdir and piddir at installation time, we create them on startup time now if they are missing. This is necessary if lockdir or piddir are a tmpfs.
author markus schnalke <meillo@marmaro.de>
date Wed, 30 May 2012 09:38:38 +0200
parents d209b4846f2b
children a19e47ebbb33
rev   line source
meillo@367 1 /*
meillo@367 2 ** MasqMail
meillo@367 3 ** Copyright (C) 1999-2001 Oliver Kurth
meillo@367 4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@367 5 **
meillo@367 6 ** This program is free software; you can redistribute it and/or modify
meillo@367 7 ** it under the terms of the GNU General Public License as published by
meillo@367 8 ** the Free Software Foundation; either version 2 of the License, or
meillo@367 9 ** (at your option) any later version.
meillo@367 10 **
meillo@367 11 ** This program is distributed in the hope that it will be useful,
meillo@367 12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@367 13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@367 14 ** GNU General Public License for more details.
meillo@367 15 **
meillo@367 16 ** You should have received a copy of the GNU General Public License
meillo@367 17 ** along with this program; if not, write to the Free Software
meillo@367 18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 19 */
meillo@0 20
meillo@0 21 #include <stdio.h>
meillo@0 22 #include <errno.h>
meillo@0 23 #include <stdlib.h>
meillo@0 24 #include <string.h>
meillo@0 25 #include <unistd.h>
meillo@0 26 #include <sys/types.h>
meillo@0 27 #include <sys/socket.h>
meillo@0 28 #include <sys/time.h>
meillo@0 29 #include <netinet/in.h>
meillo@0 30 #include <netdb.h>
meillo@0 31 #include <syslog.h>
meillo@0 32 #include <signal.h>
meillo@0 33
meillo@0 34 #include <glib.h>
meillo@0 35
meillo@0 36 #include "masqmail.h"
meillo@0 37
meillo@367 38 /*
meillo@367 39 ** mutually exclusive modes. Note that there is no 'queue daemon' mode.
meillo@367 40 ** It, as well as the distinction beween the two (non exclusive) daemon
meillo@367 41 ** (queue and listen) modes, is handled by flags.
meillo@367 42 */
meillo@281 43 enum mta_mode {
meillo@281 44 MODE_NONE = 0, /* to check if a mode was set */
meillo@281 45 MODE_ACCEPT, /* accept message on stdin (fallback mode) */
meillo@10 46 MODE_DAEMON, /* run as daemon */
meillo@10 47 MODE_RUNQUEUE, /* single queue run, online or offline */
meillo@10 48 MODE_SMTP, /* accept SMTP on stdin */
meillo@10 49 MODE_LIST, /* list queue */
meillo@10 50 MODE_MCMD, /* do queue manipulation */
meillo@10 51 MODE_VERSION, /* show version */
meillo@10 52 MODE_BI, /* fake ;-) */
meillo@281 53 };
meillo@281 54 enum mta_mode mta_mode = MODE_NONE;
meillo@0 55
meillo@0 56 char *pidfile = NULL;
meillo@0 57 volatile int sigterm_in_progress = 0;
meillo@0 58
meillo@10 59 static void
meillo@10 60 sigterm_handler(int sig)
meillo@0 61 {
meillo@10 62 if (sigterm_in_progress)
meillo@10 63 raise(sig);
meillo@10 64 sigterm_in_progress = 1;
meillo@0 65
meillo@10 66 if (pidfile) {
meillo@331 67 uid_t uid = geteuid();
meillo@331 68 if (seteuid(0) != 0) {
meillo@409 69 logwrite(LOG_ALERT, "sigterm_handler: could not set "
meillo@409 70 "euid to %d: %s\n",
meillo@409 71 0, strerror(errno));
meillo@331 72 }
meillo@10 73 if (unlink(pidfile) != 0)
meillo@409 74 logwrite(LOG_WARNING,
meillo@409 75 "could not delete pid file %s: %s\n",
meillo@409 76 pidfile, strerror(errno));
meillo@10 77 seteuid(uid); /* we exit anyway after this, just to be sure */
meillo@10 78 }
meillo@0 79
meillo@10 80 signal(sig, SIG_DFL);
meillo@10 81 raise(sig);
meillo@0 82 }
meillo@0 83
meillo@249 84 /*
meillo@367 85 ** argv: the original argv
meillo@367 86 ** argp: number of arg (may get modified!)
meillo@367 87 ** cp: pointing to the char after the option
meillo@367 88 ** e.g. `-d 6' `-d6'
meillo@367 89 ** ^ ^
meillo@249 90 */
meillo@10 91 gchar*
meillo@366 92 get_optarg(char *argv[], gint *argp, char *cp)
meillo@0 93 {
meillo@249 94 if (*cp) {
meillo@249 95 /* this kind: -xval */
meillo@249 96 return cp;
meillo@249 97 }
meillo@249 98 cp = argv[*argp+1];
meillo@249 99 if (cp && (*cp != '-')) {
meillo@249 100 /* this kind: -x val */
meillo@249 101 (*argp)++;
meillo@249 102 return cp;
meillo@10 103 }
meillo@10 104 return NULL;
meillo@0 105 }
meillo@0 106
meillo@421 107 /*
meillo@421 108 ** Create any missing directory in pathname `dir'. (Like `mkdir -p'.)
meillo@421 109 ** The code is taken from nmh.
meillo@421 110 */
meillo@421 111 gboolean
meillo@421 112 makedir_rec(char *dir, int perms)
meillo@421 113 {
meillo@421 114 char path[PATH_MAX];
meillo@421 115 char *cp, *c;
meillo@421 116 int had_an_error = 0;
meillo@421 117
meillo@421 118 c = strncpy(path, dir, sizeof(path));
meillo@421 119
meillo@421 120 while (!had_an_error && (c = strchr(c+1, '/'))) {
meillo@421 121 *c = '\0';
meillo@421 122 /* Create an outer directory. */
meillo@421 123 if (mkdir(path, perms) == -1 && errno != EEXIST) {
meillo@421 124 fprintf(stderr, "unable to create `%s': %s\n",
meillo@421 125 path, strerror(errno));
meillo@421 126 had_an_error = 1;
meillo@421 127 }
meillo@421 128 *c = '/';
meillo@421 129 }
meillo@421 130
meillo@421 131 /*
meillo@421 132 ** Create the innermost nested subdirectory of the
meillo@421 133 ** path we're being asked to create.
meillo@421 134 */
meillo@421 135 if (!had_an_error && mkdir(dir, perms)==-1 && errno != EEXIST) {
meillo@421 136 fprintf(stderr, "unable to create `%s': %s\n",
meillo@421 137 dir, strerror(errno));
meillo@421 138 had_an_error = 1;
meillo@421 139 }
meillo@421 140
meillo@421 141 return (had_an_error) ? 0 : 1;
meillo@421 142 }
meillo@421 143
meillo@10 144 gboolean
meillo@366 145 write_pidfile(gchar *name)
meillo@0 146 {
meillo@10 147 FILE *fptr;
meillo@0 148
meillo@10 149 if ((fptr = fopen(name, "wt"))) {
meillo@10 150 fprintf(fptr, "%d\n", getpid());
meillo@10 151 fclose(fptr);
meillo@10 152 pidfile = strdup(name);
meillo@10 153 return TRUE;
meillo@10 154 }
meillo@409 155 logwrite(LOG_WARNING, "could not write pid file: %s\n",
meillo@409 156 strerror(errno));
meillo@10 157 return FALSE;
meillo@10 158 }
meillo@0 159
meillo@264 160 /* on -bd or if -q has an argument */
meillo@10 161 static void
meillo@10 162 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
meillo@10 163 {
meillo@10 164 guint pid;
meillo@0 165
meillo@10 166 /* daemon */
meillo@10 167 if (!conf.run_as_user) {
meillo@10 168 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
meillo@409 169 fprintf(stderr, "must be root or %s for daemon.\n",
meillo@409 170 DEF_MAIL_USER);
meillo@262 171 exit(1);
meillo@10 172 }
meillo@10 173 }
meillo@0 174
meillo@74 175 /* reparent to init only if init is not already the parent */
meillo@74 176 if (getppid() != 1) {
meillo@74 177 if ((pid = fork()) > 0) {
meillo@262 178 exit(0);
meillo@74 179 } else if (pid < 0) {
meillo@208 180 logwrite(LOG_ALERT, "could not fork!\n");
meillo@262 181 exit(1);
meillo@74 182 }
meillo@10 183 }
meillo@0 184
meillo@10 185 signal(SIGTERM, sigterm_handler);
meillo@421 186 makedir_rec(PID_DIR, 0755);
meillo@419 187 write_pidfile(PID_DIR "/masqmail.pid");
meillo@0 188
meillo@10 189 conf.do_verbose = FALSE;
meillo@10 190
meillo@367 191 /*
meillo@367 192 ** closing and reopening the log ensures that it is open afterwards
meillo@367 193 ** because it is possible that the log is assigned to fd 1 and gets
meillo@367 194 ** thus closes by fclose(stdout). Similar for the debugfile.
meillo@72 195 */
meillo@72 196 logclose();
meillo@10 197 fclose(stdin);
meillo@10 198 fclose(stdout);
meillo@10 199 fclose(stderr);
meillo@72 200 logopen();
meillo@10 201
meillo@208 202 logwrite(LOG_NOTICE, "%s %s daemon starting\n", PACKAGE, VERSION);
meillo@409 203 listen_port(do_listen ? conf.listen_addresses : NULL,
meillo@409 204 queue_interval, argv);
meillo@0 205 }
meillo@0 206
meillo@264 207 /* -bs or called as smtpd or in.smtpd */
meillo@10 208 static void
meillo@10 209 mode_smtp()
meillo@0 210 {
meillo@10 211 /* accept smtp message on stdin */
meillo@10 212 /* write responses to stderr. */
meillo@0 213
meillo@10 214 struct sockaddr_in saddr;
meillo@10 215 gchar *peername = NULL;
meillo@10 216 int dummy = sizeof(saddr);
meillo@0 217
meillo@10 218 conf.do_verbose = FALSE;
meillo@0 219
meillo@10 220 if (!conf.run_as_user) {
meillo@331 221 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
meillo@10 222 }
meillo@0 223
meillo@10 224 DEBUG(5) debugf("accepting smtp message on stdin\n");
meillo@0 225
meillo@10 226 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
meillo@10 227 peername = g_strdup(inet_ntoa(saddr.sin_addr));
meillo@10 228 } else if (errno != ENOTSOCK)
meillo@262 229 exit(1);
meillo@0 230
meillo@10 231 smtp_in(stdin, stderr, peername, NULL);
meillo@0 232 }
meillo@0 233
meillo@264 234 /* default mode if address args or -t is specified, or called as rmail */
meillo@10 235 static void
meillo@367 236 mode_accept(address *return_path, gchar *full_sender_name, guint accept_flags,
meillo@367 237 char **addresses, int addr_cnt)
meillo@0 238 {
meillo@10 239 /* accept message on stdin */
meillo@10 240 accept_error err;
meillo@10 241 message *msg = create_message();
meillo@10 242 gint i;
meillo@266 243 pid_t pid;
meillo@0 244
meillo@83 245 if (return_path && !is_privileged_user(conf.orig_uid)) {
meillo@409 246 fprintf(stderr, "must be root, %s or in group %s for "
meillo@409 247 "setting return path.\n",
meillo@409 248 DEF_MAIL_USER, DEF_MAIL_GROUP);
meillo@262 249 exit(1);
meillo@10 250 }
meillo@0 251
meillo@10 252 if (!conf.run_as_user) {
meillo@331 253 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
meillo@10 254 }
meillo@0 255
meillo@10 256 DEBUG(5) debugf("accepting message on stdin\n");
meillo@0 257
meillo@10 258 msg->received_prot = PROT_LOCAL;
meillo@276 259
meillo@276 260 /* warn if -t option and cmdline addr args */
meillo@276 261 if (addr_cnt && (accept_flags & ACC_RCPT_FROM_HEAD)) {
meillo@409 262 logwrite(LOG_ALERT, "command line address arguments are "
meillo@409 263 "now *added* to the mail header\\\n");
meillo@409 264 logwrite(LOG_ALERT, " recipient addresses (instead of "
meillo@409 265 "substracted) when -t is given.\\\n");
meillo@276 266 logwrite(LOG_ALERT, " this changed with version 0.3.1\n");
meillo@276 267 }
meillo@276 268
meillo@10 269 for (i = 0; i < addr_cnt; i++) {
meillo@266 270 if (addresses[i][0] == '|') {
meillo@409 271 logwrite(LOG_ALERT, "no pipe allowed as recipient "
meillo@409 272 "address: %s\n", addresses[i]);
meillo@276 273 /* should we better ignore this one addr? */
meillo@262 274 exit(1);
meillo@10 275 }
meillo@409 276 msg->rcpt_list = g_list_append(msg->rcpt_list,
meillo@409 277 create_address_qualified(addresses[i],
meillo@409 278 TRUE, conf.host_name));
meillo@10 279 }
meillo@0 280
meillo@10 281 /* -f option */
meillo@10 282 msg->return_path = return_path;
meillo@0 283
meillo@10 284 /* -F option */
meillo@10 285 msg->full_sender_name = full_sender_name;
meillo@0 286
meillo@266 287 err = accept_message(stdin, msg, accept_flags);
meillo@0 288
meillo@266 289 switch (err) {
meillo@266 290 case AERR_OK:
meillo@266 291 /* to continue; all other cases exit */
meillo@266 292 break;
meillo@266 293 case AERR_EOF:
meillo@266 294 fprintf(stderr, "unexpected EOF.\n");
meillo@266 295 exit(1);
meillo@266 296 case AERR_NORCPT:
meillo@266 297 fprintf(stderr, "no recipients.\n");
meillo@266 298 exit(1);
meillo@266 299 case AERR_SIZE:
meillo@266 300 fprintf(stderr, "max message size exceeded.\n");
meillo@266 301 exit(1);
meillo@266 302 default:
meillo@266 303 /* should never happen: */
meillo@266 304 fprintf(stderr, "Unknown error (%d)\r\n", err);
meillo@266 305 exit(1);
meillo@266 306 }
meillo@266 307
meillo@266 308 if (!spool_write(msg, TRUE)) {
meillo@266 309 fprintf(stderr, "Could not write spool file\n");
meillo@266 310 exit(1);
meillo@266 311 }
meillo@266 312
meillo@267 313 /* here the mail is queued and thus in our responsibility */
meillo@409 314 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid,
meillo@409 315 addr_string(msg->return_path), prot_names[PROT_LOCAL]);
meillo@266 316
meillo@266 317 if (conf.do_queue) {
meillo@266 318 /* we're finished as we only need to queue it */
meillo@266 319 return;
meillo@266 320 }
meillo@266 321
meillo@266 322 /* deliver at once */
meillo@266 323 if ((pid = fork()) < 0) {
meillo@409 324 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n",
meillo@409 325 msg->uid);
meillo@266 326 } else if (pid == 0) {
meillo@266 327 conf.do_verbose = FALSE;
meillo@266 328 fclose(stdin);
meillo@266 329 fclose(stdout);
meillo@266 330 fclose(stderr);
meillo@266 331 if (deliver(msg)) {
meillo@266 332 exit(0);
meillo@10 333 } else {
meillo@267 334 /*
meillo@367 335 ** TODO: Should we really fail here? Because the
meillo@367 336 ** mail is queued already. If we fail the client
meillo@367 337 ** might submit it again. If at-once-delivery
meillo@367 338 ** is seen as an additional best-effort service,
meillo@367 339 ** then we should still exit successful here.
meillo@267 340 */
meillo@262 341 exit(1);
meillo@10 342 }
meillo@0 343 }
meillo@0 344 }
meillo@0 345
meillo@250 346 /*
meillo@367 347 ** if -Mrm is given
meillo@367 348 **
meillo@367 349 ** currently only the `rm' command is supported
meillo@367 350 ** until this changes, we don't need any facility for further commands
meillo@367 351 ** return success if at least one message had been deleted
meillo@250 352 */
meillo@250 353 static int
meillo@366 354 manipulate_queue(char *cmd, char *id[])
meillo@250 355 {
meillo@250 356 gboolean ok = FALSE;
meillo@250 357
meillo@250 358 if (strcmp(cmd, "rm") != 0) {
meillo@250 359 fprintf(stderr, "unknown command %s\n", cmd);
meillo@250 360 return FALSE;
meillo@250 361 }
meillo@250 362
meillo@250 363 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
meillo@250 364
meillo@250 365 /* privileged users may delete any mail */
meillo@250 366 if (is_privileged_user(conf.orig_uid)) {
meillo@250 367 for (; *id; id++) {
meillo@250 368 fprintf(stderr, "id: %s\n", *id);
meillo@250 369 if (queue_delete(*id)) {
meillo@250 370 ok = TRUE;
meillo@250 371 }
meillo@250 372 }
meillo@250 373 return ok;
meillo@250 374 }
meillo@250 375
meillo@250 376 struct passwd *pw = getpwuid(conf.orig_uid);
meillo@250 377 if (!pw) {
meillo@409 378 fprintf(stderr, "could not find a passwd entry for "
meillo@409 379 "uid %d: %s\n",
meillo@409 380 conf.orig_uid, strerror(errno));
meillo@250 381 return FALSE;
meillo@250 382 }
meillo@250 383
meillo@250 384 /* non-privileged users may only delete their own messages */
meillo@250 385 for (; *id; id++) {
meillo@349 386 message *msg = msg_spool_read(*id);
meillo@250 387
meillo@250 388 fprintf(stderr, "id: %s\n", *id);
meillo@250 389
meillo@250 390 if (!msg->ident) {
meillo@409 391 fprintf(stderr, "message %s does not have an ident\n",
meillo@409 392 *id);
meillo@250 393 continue;
meillo@250 394 }
meillo@250 395 if (strcmp(pw->pw_name, msg->ident) != 0) {
meillo@250 396 fprintf(stderr, "you do not own message id %s\n", *id);
meillo@250 397 continue;
meillo@250 398 }
meillo@250 399
meillo@378 400 if (msg->received_host || (msg->received_prot != PROT_LOCAL)) {
meillo@409 401 fprintf(stderr, "message %s was not received "
meillo@409 402 "locally\n", *id);
meillo@250 403 continue;
meillo@250 404 }
meillo@250 405
meillo@250 406 ok = queue_delete(*id);
meillo@250 407 }
meillo@250 408 return ok;
meillo@250 409 }
meillo@250 410
meillo@264 411 /* -qo, -q (without argument), or called as runq */
meillo@251 412 static int
meillo@366 413 run_queue(gboolean do_runq, gboolean do_runq_online, char *route_name)
meillo@251 414 {
meillo@251 415 int ret;
meillo@251 416
meillo@251 417 /* queue runs */
meillo@251 418 set_identity(conf.orig_uid, "queue run");
meillo@251 419
meillo@251 420 if (do_runq) {
meillo@251 421 ret = queue_run();
meillo@251 422 }
meillo@251 423
meillo@251 424 if (do_runq_online) {
meillo@251 425 if (route_name) {
meillo@409 426 conf.online_query = g_strdup_printf("/bin/echo %s",
meillo@409 427 route_name);
meillo@251 428 }
meillo@367 429 /*
meillo@367 430 ** TODO: change behavior of `-qo without argument'?
meillo@367 431 ** Because that behavior is included in -q.
meillo@367 432 */
meillo@251 433 ret = queue_run_online();
meillo@251 434 }
meillo@251 435 return ret;
meillo@251 436 }
meillo@251 437
meillo@264 438 /* -bV or default mode if neither addr arg nor -t */
meillo@261 439 static void
meillo@261 440 mode_version(void)
meillo@261 441 {
meillo@261 442 gchar *with_resolver = "";
meillo@261 443 gchar *with_auth = "";
meillo@261 444
meillo@261 445 #ifdef ENABLE_RESOLVER
meillo@261 446 with_resolver = " +resolver";
meillo@261 447 #endif
meillo@261 448 #ifdef ENABLE_AUTH
meillo@261 449 with_auth = " +auth";
meillo@261 450 #endif
meillo@261 451
meillo@378 452 printf("%s %s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth);
meillo@261 453 }
meillo@261 454
meillo@281 455 void
meillo@281 456 set_mode(enum mta_mode mode)
meillo@281 457 {
meillo@281 458 if (mta_mode && mta_mode!=mode) {
meillo@409 459 fprintf(stderr, "operation mode was already specified "
meillo@409 460 "(%d vs. %d)\n", mta_mode, mode);
meillo@281 461 exit(1);
meillo@281 462 }
meillo@281 463
meillo@281 464 mta_mode = mode;
meillo@281 465 return;
meillo@281 466 }
meillo@281 467
meillo@0 468 int
meillo@0 469 main(int argc, char *argv[])
meillo@0 470 {
meillo@251 471 gchar *progname;
meillo@366 472 char *opt;
meillo@249 473 gint arg;
meillo@0 474
meillo@10 475 gboolean do_listen = FALSE;
meillo@10 476 gboolean do_runq = FALSE;
meillo@10 477 gboolean do_runq_online = FALSE;
meillo@10 478 gboolean do_queue = FALSE;
meillo@10 479 gint queue_interval = 0;
meillo@251 480 gchar *M_cmd = NULL;
meillo@10 481 gboolean opt_t = FALSE;
meillo@10 482 gboolean opt_i = FALSE;
meillo@251 483 gchar *conf_file = CONF_FILE;
meillo@10 484 gchar *route_name = NULL;
meillo@10 485 gchar *f_address = NULL;
meillo@251 486 address *return_path = NULL; /* may be changed by -f option */
meillo@10 487 gchar *full_sender_name = NULL;
meillo@251 488 gboolean do_verbose = FALSE;
meillo@251 489 gint debug_level = -1;
meillo@0 490
meillo@260 491 /* strip the path part */
meillo@261 492 progname = strrchr(argv[0], '/');
meillo@261 493 progname = (progname) ? progname+1 : argv[0];
meillo@0 494
meillo@10 495 if (strcmp(progname, "mailq") == 0) {
meillo@10 496 mta_mode = MODE_LIST;
meillo@10 497 } else if (strcmp(progname, "mailrm") == 0) {
meillo@10 498 mta_mode = MODE_MCMD;
meillo@10 499 M_cmd = "rm";
meillo@284 500 } else if (strcmp(progname, "newaliases") == 0) {
meillo@284 501 mta_mode = MODE_BI;
meillo@10 502 } else if (strcmp(progname, "rmail") == 0) {
meillo@367 503 /*
meillo@367 504 ** the `rmail' alias should probably be removed now
meillo@367 505 ** that we have the rmail script. But let's keep it
meillo@367 506 ** for some while for compatibility. 2010-06-19
meillo@367 507 */
meillo@10 508 mta_mode = MODE_ACCEPT;
meillo@10 509 opt_i = TRUE;
meillo@284 510 } else if (strcmp(progname, "runq") == 0) {
meillo@284 511 mta_mode = MODE_RUNQUEUE;
meillo@284 512 do_runq = TRUE;
meillo@284 513 } else if (strcmp(progname, "smtpd") == 0
meillo@284 514 || strcmp(progname, "in.smtpd") == 0) {
meillo@10 515 mta_mode = MODE_SMTP;
meillo@10 516 }
meillo@0 517
meillo@10 518 /* parse cmd line */
meillo@249 519 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
meillo@251 520 opt = argv[arg] + 1; /* points to the char after the dash */
meillo@10 521
meillo@249 522 if (strcmp(opt, "-") == 0) {
meillo@249 523 /* everything after `--' are address arguments */
meillo@249 524 arg++;
meillo@249 525 break;
meillo@10 526
meillo@278 527 } else if (strcmp(opt, "bm") == 0) {
meillo@281 528 set_mode(MODE_ACCEPT);
meillo@278 529
meillo@249 530 } else if (strcmp(opt, "bd") == 0) {
meillo@281 531 set_mode(MODE_DAEMON);
meillo@249 532 do_listen = TRUE;
meillo@249 533
meillo@249 534 } else if (strcmp(opt, "bi") == 0) {
meillo@281 535 set_mode(MODE_BI);
meillo@249 536
meillo@249 537 } else if (strcmp(opt, "bs") == 0) {
meillo@281 538 set_mode(MODE_SMTP);
meillo@249 539
meillo@249 540 } else if (strcmp(opt, "bp") == 0) {
meillo@281 541 set_mode(MODE_LIST);
meillo@249 542
meillo@249 543 } else if (strcmp(opt, "bV") == 0) {
meillo@281 544 set_mode(MODE_VERSION);
meillo@249 545
meillo@249 546 } else if (strncmp(opt, "B", 1) == 0) {
meillo@249 547 /* we ignore this and throw the argument away */
meillo@249 548 get_optarg(argv, &arg, opt+1);
meillo@249 549
meillo@249 550 } else if (strncmp(opt, "C", 1) == 0) {
meillo@251 551 conf_file = get_optarg(argv, &arg, opt+1);
meillo@251 552 if (!conf_file) {
meillo@409 553 fprintf(stderr, "-C requires filename arg.\n");
meillo@262 554 exit(1);
meillo@10 555 }
meillo@249 556
meillo@249 557 } else if (strncmp(opt, "d", 1) == 0) {
meillo@249 558 if (getuid() != 0) {
meillo@409 559 fprintf(stderr, "only root may set the "
meillo@409 560 "debug level.\n");
meillo@262 561 exit(1);
meillo@249 562 }
meillo@249 563 char *lvl = get_optarg(argv, &arg, opt+1);
meillo@249 564 if (!lvl) {
meillo@409 565 fprintf(stderr, "-d requires number arg.\n");
meillo@262 566 exit(1);
meillo@249 567 }
meillo@249 568 debug_level = atoi(lvl);
meillo@249 569
meillo@249 570 } else if (strncmp(opt, "f", 1) == 0) {
meillo@249 571 /* set return path */
meillo@249 572 gchar *address = get_optarg(argv, &arg, opt+1);
meillo@249 573 if (!address) {
meillo@409 574 fprintf(stderr, "-f requires address arg.\n");
meillo@262 575 exit(1);
meillo@249 576 }
meillo@249 577 f_address = g_strdup(address);
meillo@249 578
meillo@249 579 } else if (strncmp(opt, "F", 1) == 0) {
meillo@249 580 full_sender_name = get_optarg(argv, &arg, opt+1);
meillo@249 581 if (!full_sender_name) {
meillo@409 582 fprintf(stderr, "-F requires name arg.\n");
meillo@262 583 exit(1);
meillo@249 584 }
meillo@249 585
meillo@249 586 } else if (strcmp(opt, "i") == 0) {
meillo@249 587 opt_i = TRUE;
meillo@249 588
meillo@249 589 } else if (strcmp(opt, "m") == 0) {
meillo@249 590 /* ignore -m (me too) switch (see man page) */
meillo@249 591
meillo@249 592 } else if (strcmp(opt, "Mrm") == 0) {
meillo@281 593 set_mode(MODE_MCMD);
meillo@249 594 M_cmd = "rm";
meillo@249 595
meillo@249 596 } else if (strcmp(opt, "odq") == 0) {
meillo@249 597 do_queue = TRUE;
meillo@249 598
meillo@249 599 } else if (strcmp(opt, "oi") == 0) {
meillo@257 600 opt_i = TRUE;
meillo@249 601
meillo@249 602 } else if (strncmp(opt, "o", 1) == 0) {
meillo@249 603 /* ignore all other -oXXX options */
meillo@249 604
meillo@249 605 } else if (strncmp(opt, "qo", 2) == 0) {
meillo@278 606 /* must be before the `q' check */
meillo@281 607 set_mode(MODE_RUNQUEUE);
meillo@249 608 do_runq_online = TRUE;
meillo@249 609 /* can be NULL, then we use online detection method */
meillo@290 610 /* TODO: behavior might change if it is NULL */
meillo@249 611 route_name = get_optarg(argv, &arg, opt+2);
meillo@290 612 if (!route_name) {
meillo@409 613 fprintf(stderr, "Please do not use -qo "
meillo@409 614 "without argument anymore; "
meillo@409 615 "use -q instead.\n");
meillo@409 616 fprintf(stderr, "The behavior for -qo without "
meillo@409 617 "argument is likely to "
meillo@409 618 "change.\n");
meillo@290 619 }
meillo@249 620
meillo@249 621 } else if (strncmp(opt, "q", 1) == 0) {
meillo@249 622 /* must be after the `qo' check */
meillo@249 623 gchar *optarg;
meillo@249 624
meillo@251 625 optarg = get_optarg(argv, &arg, opt+1);
meillo@251 626 if (optarg) {
meillo@409 627 /* do regular queue runs */
meillo@281 628 set_mode(MODE_DAEMON);
meillo@254 629 queue_interval = time_interval(optarg);
meillo@281 630 } else {
meillo@409 631 /* do a single queue run */
meillo@281 632 set_mode(MODE_RUNQUEUE);
meillo@281 633 do_runq = TRUE;
meillo@249 634 }
meillo@249 635
meillo@249 636 } else if (strcmp(opt, "t") == 0) {
meillo@249 637 opt_t = TRUE;
meillo@249 638
meillo@249 639 } else if (strcmp(opt, "v") == 0) {
meillo@249 640 do_verbose = TRUE;
meillo@249 641
meillo@10 642 } else {
meillo@249 643 fprintf(stderr, "unrecognized option `-%s'\n", opt);
meillo@262 644 exit(1);
meillo@10 645 }
meillo@0 646 }
meillo@0 647
meillo@281 648 if (!mta_mode && arg==argc && !opt_t) {
meillo@278 649 /*
meillo@367 650 ** In this case no rcpts can be found, thus no mail
meillo@367 651 ** can be sent, thus masqmail will always fail. We
meillo@367 652 ** rather do something better instead. This covers
meillo@367 653 ** also the case of calling masqmail without args.
meillo@278 654 */
meillo@281 655 mode_version();
meillo@281 656 exit(0);
meillo@259 657 }
meillo@259 658
meillo@10 659 if (mta_mode == MODE_VERSION) {
meillo@261 660 mode_version();
meillo@262 661 exit(0);
meillo@10 662 }
meillo@0 663
meillo@281 664 if (!mta_mode) {
meillo@281 665 mta_mode = MODE_ACCEPT;
meillo@281 666 }
meillo@281 667
meillo@10 668 /* initialize random generator */
meillo@10 669 srand(time(NULL));
meillo@10 670 /* ignore SIGPIPE signal */
meillo@10 671 signal(SIGPIPE, SIG_IGN);
meillo@0 672
meillo@73 673 /* close all possibly open file descriptors, except std{in,out,err} */
meillo@10 674 {
meillo@10 675 int i, max_fd = sysconf(_SC_OPEN_MAX);
meillo@0 676
meillo@251 677 if (max_fd <= 0) {
meillo@10 678 max_fd = 64;
meillo@251 679 }
meillo@251 680 for (i=3; i<max_fd; i++) {
meillo@10 681 close(i);
meillo@251 682 }
meillo@10 683 }
meillo@0 684
meillo@10 685 init_conf();
meillo@0 686
meillo@367 687 /*
meillo@367 688 ** if we are not privileged, and the config file was changed we
meillo@367 689 ** implicetely set the the run_as_user flag and give up all
meillo@367 690 ** privileges.
meillo@367 691 **
meillo@367 692 ** So it is possible for a user to run his own daemon without
meillo@367 693 ** breaking security.
meillo@367 694 */
meillo@251 695 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
meillo@421 696 logwrite(LOG_NOTICE, "Changing to run_as_user.\n");
meillo@251 697 conf.run_as_user = TRUE;
meillo@331 698 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
meillo@331 699 if (setgid(conf.orig_gid)) {
meillo@409 700 logwrite(LOG_ALERT, "could not set gid to %d: %s\n",
meillo@409 701 conf.orig_gid, strerror(errno));
meillo@331 702 exit(1);
meillo@331 703 }
meillo@331 704 if (setuid(conf.orig_uid)) {
meillo@409 705 logwrite(LOG_ALERT, "could not set uid to %d: %s\n",
meillo@409 706 conf.orig_uid, strerror(errno));
meillo@331 707 exit(1);
meillo@331 708 }
meillo@10 709 }
meillo@0 710
meillo@155 711 conf.log_dir = LOG_DIR;
meillo@397 712 conf.debug_level = debug_level; /* for debuggin during read_conf() */
meillo@288 713 /* FIXME: fails if we run as user */
meillo@155 714 logopen();
meillo@155 715 if (!read_conf(conf_file)) {
meillo@409 716 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading "
meillo@409 717 "config\n");
meillo@155 718 exit(5);
meillo@155 719 }
meillo@155 720 logclose();
meillo@0 721
meillo@251 722 if (do_queue) {
meillo@10 723 conf.do_queue = TRUE;
meillo@251 724 }
meillo@251 725 if (do_verbose) {
meillo@10 726 conf.do_verbose = TRUE;
meillo@251 727 }
meillo@251 728 if (debug_level >= 0) { /* if >= 0, it was given by argument */
meillo@10 729 conf.debug_level = debug_level;
meillo@251 730 }
meillo@0 731
meillo@367 732 /*
meillo@367 733 ** It appears that changing to / ensures that we are never in
meillo@367 734 ** a directory which we cannot access. This situation could be
meillo@367 735 ** possible after changing identity.
meillo@367 736 ** Maybe we should only change to / if we not run as user, to
meillo@367 737 ** allow relative paths for log files in test setups for
meillo@367 738 ** instance.
meillo@46 739 */
meillo@10 740 chdir("/");
meillo@0 741
meillo@10 742 if (!conf.run_as_user) {
meillo@10 743 if (setgid(0) != 0) {
meillo@409 744 fprintf(stderr, "could not set gid to 0. "
meillo@409 745 "Is the setuid bit set? : %s\n",
meillo@409 746 strerror(errno));
meillo@262 747 exit(1);
meillo@10 748 }
meillo@10 749 if (setuid(0) != 0) {
meillo@409 750 fprintf(stderr, "could not gain root privileges. "
meillo@409 751 "Is the setuid bit set? : %s\n",
meillo@409 752 strerror(errno));
meillo@262 753 exit(1);
meillo@10 754 }
meillo@10 755 }
meillo@0 756
meillo@421 757 if (conf.run_as_user) {
meillo@421 758 logwrite(LOG_NOTICE, "Using spool directory `%s' for "
meillo@421 759 "lock files.\n", conf.spool_dir);
meillo@421 760 conf.lock_dir = conf.spool_dir;
meillo@421 761 } else {
meillo@421 762 int olduid, oldgid;
meillo@421 763
meillo@421 764 set_euidgid(conf.mail_uid, conf.mail_gid, &olduid, &oldgid);
meillo@421 765 makedir_rec(conf.lock_dir, 0775);
meillo@421 766 set_euidgid(olduid, oldgid, NULL, NULL);
meillo@421 767 }
meillo@421 768
meillo@10 769 if (!logopen()) {
meillo@10 770 fprintf(stderr, "could not open log file\n");
meillo@262 771 exit(1);
meillo@10 772 }
meillo@0 773
meillo@415 774 DEBUG(1) debugf("----STARTING---- masqmail %s\n", VERSION);
meillo@0 775
meillo@10 776 DEBUG(5) {
meillo@10 777 gchar **str = argv;
meillo@10 778 debugf("args: \n");
meillo@10 779 while (*str) {
meillo@10 780 debugf("%s \n", *str);
meillo@10 781 str++;
meillo@10 782 }
meillo@10 783 }
meillo@10 784 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
meillo@0 785
meillo@10 786 if (f_address) {
meillo@409 787 return_path = create_address_qualified(f_address, TRUE,
meillo@409 788 conf.host_name);
meillo@10 789 g_free(f_address);
meillo@10 790 if (!return_path) {
meillo@409 791 fprintf(stderr, "invalid RFC821 address: %s\n",
meillo@409 792 f_address);
meillo@262 793 exit(1);
meillo@10 794 }
meillo@10 795 }
meillo@10 796
meillo@10 797 switch (mta_mode) {
meillo@10 798 case MODE_DAEMON:
meillo@10 799 mode_daemon(do_listen, queue_interval, argv);
meillo@10 800 break;
meillo@251 801
meillo@10 802 case MODE_RUNQUEUE:
meillo@251 803 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
meillo@10 804 break;
meillo@10 805
meillo@10 806 case MODE_SMTP:
meillo@10 807 mode_smtp();
meillo@10 808 break;
meillo@10 809
meillo@10 810 case MODE_LIST:
meillo@10 811 queue_list();
meillo@10 812 break;
meillo@10 813
meillo@10 814 case MODE_BI:
meillo@262 815 exit(0);
meillo@10 816 break; /* well... */
meillo@10 817
meillo@10 818 case MODE_MCMD:
meillo@250 819 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
meillo@10 820 break;
meillo@10 821
meillo@10 822 case MODE_ACCEPT:
meillo@10 823 {
meillo@409 824 guint accept_flags = 0;
meillo@409 825 accept_flags |= (opt_t ? ACC_RCPT_FROM_HEAD : 0);
meillo@409 826 accept_flags |= (opt_i ?
meillo@409 827 ACC_DOT_IGNORE : ACC_NODOT_RELAX);
meillo@409 828 mode_accept(return_path, full_sender_name,
meillo@409 829 accept_flags, argv + arg, argc - arg);
meillo@258 830 exit(0);
meillo@10 831 }
meillo@10 832 break;
meillo@251 833
meillo@10 834 default:
meillo@10 835 fprintf(stderr, "unknown mode: %d\n", mta_mode);
meillo@10 836 break;
meillo@0 837 }
meillo@0 838
meillo@10 839 logclose();
meillo@0 840
meillo@263 841 exit(0);
meillo@0 842 }