This is modes.c in view mode; [Download] [Up]
/* @(#)src/modes.c 1.3 02 Dec 1990 03:46:28 */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * modes.c: * routines to handle the various modes of operation. Typically, * these are major functions called directly from main. * * external functions: build_host_strings, compute_nobody, * input_signals, processing_signals, * deliver_signals, test_addresses, * perform_deliver_mail, deliver_mail, * daemon_mode, noop_mode, verify_addresses, * print_version, print_copying_file, * print_variables, print_queue, smtp_mode, * fork_wait */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <pwd.h> #include <signal.h> #include <time.h> #include <errno.h> #include "defs.h" #if defined(HAVE_BSD_NETWORKING) # ifdef UNIX_CPC # include <h/types42.h> # include <h/socket.h> # else # include <sys/socket.h> # endif /* not UNIX_CPC */ # include <netinet/in.h> # include <netdb.h> # include <fcntl.h> #endif #include "config.h" #include "smail.h" #include "alloc.h" #include "dys.h" #include "addr.h" #include "hash.h" #include "main.h" #include "log.h" #include "transport.h" #include "child.h" #include "exitcodes.h" #ifndef DEPEND # include "extern.h" # include "debug.h" # include "error.h" #endif /* variables exported from this file */ int daemon_pid = 0; /* functions local to this file */ static int start_daemon(); static void bg_run_queue(); static void do_run_queue(); static void sig_unlink(); static void sig_close(); static void set_queue_only(); static void do_smtp(); /* * build_host_strings - build the various types of hostnames * * always build primary_name. Build, if NULL, uucp_name, hostnames, * and visible_name. */ void build_host_strings() { char *s; if (hostnames == NULL || uucp_name == NULL) { char *real_hostname = compute_hostname(); if (real_hostname == NULL) { /* the machine doesn't know who he is */ panic(EX_SOFTWARE, "build_host_strings: Configuration error: hostname unknown"); /*NOTREACHED*/ } if (uucp_name == NULL) { /* uucp_name is exactly the real hostname by default */ uucp_name = real_hostname; } if (hostnames == NULL) { /* * by default hostnames is constructed from the real hostname * and the visible_domains list. If visible_domains is NULL, * then hostnames is exactly the real hostname. */ if (visible_domains == NULL || visible_domains[0] == '\0') { hostnames = real_hostname; } else { register char *domain = strcolon(visible_domains); struct str str; /* build hostnames here */ STR_INIT(&str); str_printf(&str, "%s.%s", real_hostname, domain); while (domain = strcolon((char *)NULL)) { str_printf(&str, ":%s.%s", real_hostname, domain); } STR_NEXT(&str, '\0'); STR_DONE(&str); hostnames = str.p; } } } /* primary_name is always the first hostname value */ primary_name = hostnames; s = index(hostnames, ':'); if (s) { /* In ANSI C string literals can be put in unwritable text space. * Thus, rather than just put a nul byte to separate primary_name * and hostnames, we must malloc something and build the * primary_name */ char *new_pd = xmalloc(s - primary_name + 1); (void) memcpy(new_pd, primary_name, s - primary_name); new_pd[s - primary_name] = '\0'; primary_name = new_pd; } /* visible_name is the primary_name by default */ if (visible_name == NULL) { visible_name = primary_name; } } /* * compute_nobody - figure out the nobody uid/gid * * if `nobody_uid' and `nobody_gid' are defined, use them, otherwise * use the login name in `nobody' to determine nobody_uid/gid. */ void compute_nobody() { if (nobody_uid != BOGUS_USER && nobody_gid != BOGUS_GROUP) { return; } if (nobody == NULL || nobody[0] == '\0') { /* * nobody uid/gid not defined. use something likely to not be * in use */ nobody_uid = 32767; nobody_gid = 32767; } else { struct passwd *pw; /* passwd entry for `nobody' */ pw = getpwbyname(nobody); if (pw == NULL) { nobody_uid = 32767; nobody_gid = 32767; } else { nobody_uid = pw->pw_uid; nobody_gid = pw->pw_gid; } } } /* * input_signals - setup signals to use when reading a message from stdin * * when reading in a message (for DELIVER_MAIL mode), the spool file should * be removed if a SIGHUP or SIGINT comes in, as this supposedly indicates * that the user did not complete his input message. If a SIGTERM comes * in then set the queue_only flag, to avoid taking up lots of time. */ void input_signals() { if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { (void) signal(SIGHUP, sig_unlink); } if (signal(SIGINT, SIG_IGN) != SIG_IGN) { (void) signal(SIGINT, sig_unlink); } (void) signal(SIGALRM, SIG_IGN); (void) signal(SIGTERM, set_queue_only); } /* * processing_signals - signals to use when processing a message * * in this case, ignore hangups but still allow the user to send an * interrupt (if mode is DELIVER_MAIL), up until the time delivery is * started. SIGTERM will close the spool file for now. */ void processing_signals() { (void) signal(SIGHUP, SIG_IGN); if (signal(SIGINT, SIG_IGN) != SIG_IGN) { if (operation_mode == DELIVER_MAIL) { (void) signal(SIGINT, sig_unlink); } else { (void) signal(SIGINT, sig_close); } } (void) signal(SIGALRM, SIG_IGN); (void) signal(SIGTERM, sig_close); } /* * delivery_signals - signals to use when delivering a message * * in this case, ignore everything to avoid stopping in awkward states. * * TODO: perhaps SIGTERM should set a flag to cause smail to exit between * calls to transport drivers. Inbetween calls, the state will not * be inconsistent and it should be okay to call close_spool(). */ void delivery_signals() { (void) signal(SIGHUP, SIG_IGN); (void) signal(SIGINT, SIG_IGN); (void) signal(SIGINT, SIG_IGN); (void) signal(SIGTERM, SIG_IGN); } /* * sig_unlink - handle a signal by unlinking the spool file. * * we assume this means that a user didn't really want to send a message * after all so we remove the spooled message and exit. */ static void sig_unlink(sig) int sig; { (void) signal(sig, SIG_IGN); unlink_spool(); write_log(LOG_TTY, "interrupt: mail message removed"); exit(EX_OSERR); } /* * set_queue_only - handle a signal by setting the flag queue_only * * this will cause the message to be read in, but not processed. Thus, * the amount of time spent on processing the message is minimized, while * full message processing can be attempted later. */ static void set_queue_only(sig) { (void) signal(sig, set_queue_only); queue_only = TRUE; } /* * sig_close - handle a signal by closing the spool file. * * this will cause processing to stop for the current message. However, * it should be restartable later from a queue run. */ static void sig_close(sig) int sig; { (void) signal(sig, SIG_IGN); close_spool(); exit(0); /* this is not yet an error */ } /* * test_addresses - read addrs from stdin and route them, for fun * * Call parse_address and route_remote_addrs to determine which transport * is going to be used, and what it will be given, for addresses given on * stdin. */ void test_addresses() { char line[4096]; /* plenty of space for input lines */ int stdin_is_a_tty = isatty(0); X_PANIC_OKAY(); if (primary_name == NULL) { /* setup all of the hostname information */ build_host_strings(); } while (stdin_is_a_tty && fputs("> ", stdout), gets(line)) { struct addr *cur = alloc_addr(); struct addr *done = NULL; struct addr *defer = NULL; struct addr *fail = NULL; char *error; int form; cur->in_addr = line; if ((cur->work_addr = preparse_address(line, &error)) == NULL) { write_log(LOG_TTY, "syntax error in address: %s", error); continue; } while (cur && (form = parse_address(cur->work_addr, &cur->target, &cur->remainder) ) != FAIL && form != LOCAL) { cur->flags &= ~ADDR_FORM_MASK; cur->flags |= form; if (islocalhost(cur->target)) { cur->work_addr = cur->remainder; continue; } done = NULL; defer = NULL; fail = NULL; route_remote_addrs(cur, &done, &cur, &defer, &fail); } if (defer) { (void) fprintf(stderr, "%s ... temporary failure: %s\n", defer->in_addr, defer->error->message); continue; } if (fail) { (void) fprintf(stderr, "%s ... failed: %s\n", fail->in_addr, fail->error->message); continue; } if (done) { (void) printf("host: %s user: %s transport: %s\n", done->next_host, done->next_addr, done->transport->name); continue; } switch (form) { case FAIL: (void) fprintf(stderr, "%s ... parse error: %s\n", cur->in_addr, cur->remainder); break; case LOCAL: (void) printf("user: %s transport: local\n", cur->remainder); break; default: (void) fprintf(stderr, "%s ... internal error in resolve_addr_list\n", line); break; } } } /* * perform_deliver_mail - read in a message and call deliver_mail() * * Build a queue file using a message on stdin. Then, if we are * performing immediate delivery of messages, call deliver_mail() to * deliver the message. */ void perform_deliver_mail() { char *error; /* setup signals to remove the spool file on errors */ X_NO_PANIC(); input_signals(); if (queue_message(stdin, dot_usage, recipients, &error) == FAIL) { open_system_logs(); log_spool_errors(); panic(EX_OSFILE, "incoming mail lost: %s: %s", error, strerrno()); /*NOTREACHED*/ } X_PANIC_OKAY(); /* * if we are running as rmail or rsmtp, then always return a zero * exitstatus for errors that occur after successfully spooling * the message. Otherwise, the UUCP subsystem (which calls rmail * or rsmtp for mail delivery) may return error messages to the * sender, even though smail will now be in complete control of * error handling on this message. */ if (prog_type == PROG_RMAIL || prog_type == PROG_RSMTP) { force_zero_exitvalue = TRUE; } if (read_message() == NULL) { panic(EX_OSFILE, "failed to read queued message"); /*NOTREACHED*/ } /* * if a precedence: header is given * then change the grade for the mail message */ check_grade(); /* * up til now keyboard signals would have caused mail to be * removed. Now that we actually have the message, setup * signals appropriate for guarranteeing delivery or panics * on errors. */ processing_signals(); /* * open the system and per message log files. * Do this after spool_message so that panic errors while opening * the log files do not dump the mail on the floor. */ open_system_logs(); /* * make a log entry for the new message */ log_incoming(); /* log errors generated in spooling the message */ log_spool_errors(); /* if we are only queuing, we have gone as far as we need to */ if (queue_only || deliver_mode == QUEUE_MESSAGE) { if (debug && dont_deliver) { /* unless we are debugging as well (with -d, not -v) */ DEBUG(DBG_MAIN_LO, "debugging is on, -Q (queue_only) flag ignored\n"); } else { if (debug) { DEBUG(DBG_MAIN_LO, "-Q (queue_only) specified and message is queued\n"); } close_spool(); return; } } /* * if we are delivering in background, fork a child to perform * delivery and exit. Ignore this when debugging. */ if (deliver_mode == BACKGROUND && debug == 0) { int pid; /* unlock the message in the parent, see lock_message() for details */ delivery_signals(); /* disassociate from terminal */ if (error_processing != TERMINAL) { (void) fclose(stdin); } unlock_message(); pid = fork(); if (pid < 0) { /* fork failed, just leave the queue file there and exit */ write_log(LOG_TTY, "fork failed: %s, message queued", strerrno()); close_spool(); return; } if (pid > 0) { /* in parent process, just return */ return; } #ifdef POSIX_OS (void) setsid(); #else /* not POSIX_OS */ (void) setpgrp(0, getpid()); #endif /* POSIX_OS */ if (lock_message() == FAIL) { /* somebody else grabbed the lock, let them deliver */ return; } } /* read the various configuration files */ if ((error = read_transport_file()) || (error = read_router_file()) || (error = read_director_file()) || (error = read_qualify_file())) { panic(EX_OSFILE, "%s", error); } /* setup all of the hostname information */ build_host_strings(); /* figure out the nobody uid/gid */ compute_nobody(); /* * process the message, find all of the recipients and * perform delivery. */ deliver_mail(); /* * close the system-wide log files */ close_system_logs(); } /* * deliver_mail - oversee the delivery of mail (default mailer operation) * * Spool the mail, process the header, process the addresses, route, * alias expand, deliver remote mail, deliver local mail, process errors. */ void deliver_mail() { struct addr *cur; /* addr being processed */ struct addr *next; /* next addr to process */ struct addr *fail= NULL; /* list of failed addrs */ struct addr *defer = NULL; /* list of deferred addrs */ struct addr *deliver; /* addr structures ready to deliver */ /* transport instances */ struct assign_transport *assigned_transports = NULL; char *error; struct identify_addr *sent_errors; /* addresses previously sent errors */ struct defer_addr *defer_errors; /* previous defer messages */ /* * preparse all of the recipient addresses given as arguments. * If we are extracting addresses from the header, then * these addresses are NOT to receive the mail. To accomplish * this, add them to the hash table so they will be ignored * later. */ for (cur = recipients, recipients = NULL; cur; cur = next) { next = cur->succ; if ((cur->work_addr = preparse_address(cur->in_addr, &error)) == NULL) { /* * ERR_147 - parse error in input address * * DESCRIPTION * preparse_address() encountered a parsing error in one of * the addresses supplied by the sender. The specific * error was returned in `error'. * * ACTIONS * Fail the address and send an error to the sender. * * RESOLUTION * The sender should supply a valid address. */ cur->error = note_error(ERR_NSENDER|ERR_147, xprintf("parse error %s", error)); cur->flags &= ~ADDR_FORM_MASK; cur->flags |= PARSE_ERROR; cur->succ = fail; fail = cur; continue; } cur->succ = recipients; recipients = cur; if (extract_addresses) { (void) add_to_hash(cur->work_addr, (char *)NULL, 0, hit_table); xfree(cur->work_addr); /* don't need it anymore */ xfree((char *)cur); cur = cur->succ; continue; } cur = cur->succ; } if (extract_addresses) { recipients = NULL; /* don't need them anymore */ } /* * process_header will perform a preliminary analysis of the * header fields. It will note which standard fields exist * and may take addresses from the header. It will perform * some initial processing of the From: lines and, depending * upon configuration, may put `,' characters between addresses. * Also, some required fields which do not exist will be * added, (i.e., From: and To: and Message-Id:). */ if (extract_addresses) { error = process_header(&recipients); } else { error = process_header((struct addr **)NULL); } if (error) { write_log(LOG_MLOG, "error in header: %s", error); if (extract_addresses) { return_to_sender = TRUE; /* notify people of errors, ignoring previously reported errors */ sent_errors = NULL; defer_errors = NULL; (void) process_msg_log((struct addr *)NULL, &sent_errors, &defer_errors); notify((struct addr *)NULL, /* no defer or fail list */ (struct addr *)NULL, sent_errors); unlink_spool(); } return; } /* * given the list of recipient addresses, turn those * addresses into more specific destinations, including * the transport that is to be used, in the case of * addresses destined remote */ deliver = NULL; resolve_addr_list(recipients, &deliver, &defer, &fail, TRUE); /* * remove addresses to which we have already delivered mail and * note addresses for which we have already delivered error messages */ sent_errors = NULL; defer_errors = NULL; deliver = process_msg_log(deliver, &sent_errors, &defer_errors); /* log failures right now */ if (fail) { fail_delivery(fail); } /* * assign instances of transports for remote addresses */ assigned_transports = assign_transports(deliver); /* * deliver all of the assigned mail. Note: call_transport * will already have handled log entries for failed addresses. */ delivery_signals(); call_transports(assigned_transports, &defer, &fail); if (defer) { defer_delivery(defer, defer_errors); } /* * perform error notification for all failed and perhaps some deferred * addresses. Addresses for which we have already sent error messages * are ignored. */ notify(defer, fail, sent_errors); /* * tidy up before going away */ if (call_defer_message) { /* * leave a file in an error/ directory for the system * administrator to look at. This is used for failed error * mail and for problems resulting from configuration errors. */ defer_message(); } else if (some_deferred_addrs) { /* * leave the file around to be processed by a later queue run. * Use this for temporary problems such as being blocked by a * locked file, or timeouts waiting for a response from a * remote system. */ close_spool(); } else { /* * if no requests for deferring of addresses or of the message * occured, then we are done with the message. Thus, unlink * the message and the per-message log file. */ unlink_spool(); unlink_msg_log(); } } #if !defined(SIGCHLD) && defined(SIGCLD) /* System V uses a different name */ # define SIGCHLD SIGCLD #endif static void daemon_sighup(); #ifdef SIGCHLD static void daemon_sigchld(); #endif static void daemon_sigalrm(); static int got_sighup; static int got_sigalrm; #if defined(HAVE_BSD_NETWORKING) static void do_daemon_accept(); /* * daemon_mode - be a daemon waiting for requests * * Listen on the smtp port for connections. Accept these connections and * read smtp commands from them. */ void daemon_mode() { int ls; /* listen socket */ int as; /* accept socket */ struct sockaddr_in sin, from; /* from is currently */ struct servent *smtp_service; /* smtp service file entry */ X_PANIC_OKAY(); /* we aren't interested in stdin or stdout */ (void) close(0); (void) close(1); /* setup the listen socket */ if ((smtp_service = getservbyname("smtp", "tcp")) == NULL) { write_log(LOG_TTY, "smtp/tcp: unknown service"); exitvalue = EX_UNAVAILABLE; return; } (void) bzero((char *)&sin, sizeof(sin)); ls = socket(AF_INET, SOCK_STREAM, 0); if (ls < 0) { write_log(LOG_TTY, "socket(AF_INET, SOCKSTREAM, 0) failed: %s", strerrno()); exitvalue = EX_OSERR; return; } sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = smtp_service->s_port; if (bind(ls, (struct sockaddr *)&sin, sizeof(sin)) < 0) { write_log(LOG_TTY, "bind() failed: %s\n", strerrno()); exitvalue = EX_OSERR; return; } /* * start smail as a background daemon process. Return in the * parent. */ if (start_daemon() != 0) { return; } # ifdef SIGCHLD /* NOTE: If there is no SIGCHLD we can't reap dead kids! lose */ (void) signal(SIGCHLD, daemon_sigchld); # endif /* if we are doing queue runs, do one queue run first */ if (process_queue) { bg_run_queue(); } /* SIGHUP means re-exec */ got_sighup = FALSE; (void) signal(SIGHUP, daemon_sighup); /* set the alarm for wakeup time */ got_sigalrm = FALSE; if (process_queue && queue_interval > 0) { (void) signal(SIGALRM, daemon_sigalrm); (void) alarm(queue_interval); } /* loop processing connect requests or alarm signals */ (void) listen(ls, 5); for (;;) { int len = sizeof(from); DEBUG1(DBG_MAIN_MID, "listening on port %d...\n", smtp_service->s_port); /* get connection */ as = accept(ls, (struct sockaddr *)&from, &len); if (as < 0 && errno != EINTR) { write_log(LOG_PANIC, "accept failed: %s", strerrno()); continue; } if (as >= 0) { do_daemon_accept(ls, as, &from); } if (got_sighup) { write_log(LOG_SYS, "pid %d: SIGHUP received, exec(%s)", getpid(), smail); execv(smail, save_argv); panic(EX_UNAVAILABLE, "pid %d: exec of %s failed", getpid()); } if (got_sigalrm) { /* if config file have changed, recycle */ DEBUG(DBG_MAIN_LO, "SIGALRM received, check input queue\n"); if (is_newconf()) { /* re-exec smail */ write_log(LOG_SYS, "pid %d: new config files, exec(%s)", getpid(), smail); execv(smail, save_argv); panic(EX_UNAVAILABLE, "pid %d: exec of %s failed", getpid()); /*NOTREACHED*/ } /* reopen the log files so that they can be moved and removed */ close_system_logs(); open_system_logs(); /* recache all of the driver info, to get any changes */ #ifdef SIGCHLD (void) signal(SIGCHLD, SIG_DFL); #endif cache_directors(); cache_routers(); cache_transports(); #ifdef SIGCHLD (void) signal(SIGCHLD, daemon_sigchld); #endif bg_run_queue(); /* do queue run in child process */ got_sigalrm = FALSE; (void) alarm(queue_interval); } } } /* * do_daemon_accept - perform processing for an accepted SMTP connection * * accept SMTP commands in a separate process. */ static void do_daemon_accept(ls, fd, from) int ls; /* listen socket, must be closed */ int fd; /* connected channel */ struct sockaddr_in *from; /* address of peer */ { int fd2; /* dup of connected channel */ int pid; DEBUG1(DBG_MAIN_LO, "connection request from [%s]\n", inet_ntoa(from->sin_addr)); fd2 = dup(fd); if (fd2 < 0) { FILE *f = fdopen(fd, "w"); (void) fprintf(f, "421 Connection refused: %s\r\n", strerrno()); (void) fflush(f); (void) close(fd); return; } pid = fork(); if (pid == 0) { FILE *in; /* input channel */ FILE *out; /* output channel */ /* in child process */ (void) close(ls); in = fdopen(fd, "r"); /* setup the channels */ out = fdopen(fd2, "w"); /* no longer ignore kids */ # ifdef SIGCHLD (void) signal(SIGCHLD, SIG_DFL); # endif /* do the actual work */ do_smtp(in, out); /* done with that transaction */ exit(0); } /* in parent process */ if (pid < 0) { FILE *f = fdopen(fd, "w"); (void) fprintf(f, "421 Connection refused: %s\r\n", strerrno()); (void) fflush(f); } (void) close(fd); (void) close(fd2); } #else /* not defined(HAVE_BSD_NETWORKING) */ /* * For systems that don't have sockets, turn daemon mode into * a call to noop_mode(). This will have the desired affect if a * queue run was also requested. Otherwise, it will simply return. */ void daemon_mode() { if (errfile) { (void) fprintf(stderr, "%s: daemon mode not supported\n", program); exitvalue = EX_UNAVAILABLE; } noop_mode(); } #endif /* not defined(HAVE_BSD_NETWORKING) */ /* * daemon_sighup - note that we received a SIGHUP signal */ static void daemon_sighup() { got_sighup = TRUE; (void) signal(SIGHUP, daemon_sighup); } #ifdef SIGCHLD /* * daemon_sigchld - reap dead kids */ static void daemon_sigchld() { (void) wait((int *)0); /* for System V we setup signal again */ (void) signal(SIGCHLD, daemon_sigchld); } #endif /* SIGCHLD */ /* * daemon_sigalrm - note that we received a SIGALRM signal */ static void daemon_sigalrm() { got_sigalrm = TRUE; (void) signal(SIGALRM, daemon_sigalrm); } /* * noop_mode - perform queue runs once or at intervals * * When the -q flag is specified, or smail is invoked as runq, but -bd * is not specified, then noop_mode() is invoked, which does nothing but * execute run_queue() in background at intervals. If no sleep interval * is specified, run_queue() is called only once. */ void noop_mode() { X_PANIC_OKAY(); if (! process_queue) { /* queue procesing not requested, nothing to do */ return; } /* * Turn smail process into a daemon, quit if we are in the parent * process. */ if (start_daemon() != 0) { return; } /* arrange signals */ got_sighup = FALSE; got_sigalrm = FALSE; #ifdef SIGCHLD /* NOTE: If there is no SIGCHLD we can't reap dead kids! lose */ (void) signal(SIGCHLD, daemon_sigchld); #endif (void) signal(SIGHUP, daemon_sighup); (void) signal(SIGALRM, daemon_sigalrm); if (debug && queue_interval == 0) { #ifdef SIGCHLD (void) signal(SIGCHLD, SIG_DFL); #endif do_run_queue(); } else { bg_run_queue(); } if (queue_interval > 0) { /* get an alarm at intervals */ (void) alarm(queue_interval); for (;;) { pause(); /* watch for SIGHUP to indicate a recycle */ if (got_sighup) { write_log(LOG_SYS, "pid %d: SIGHUP received, exec(%s)", getpid(), smail); execv(smail, save_argv); panic(EX_UNAVAILABLE, "pid %d: exec of %s failed", getpid()); /*NOTREACHED*/ } if (! got_sigalrm) { continue; } /* reset the alarm condition */ got_sigalrm = FALSE; /* if config file have changed, recycle */ if (is_newconf()) { write_log(LOG_SYS, "pid %d: new config files, exec(%s)", getpid(), smail); execv(smail, save_argv); panic(EX_UNAVAILABLE, "pid %d: exec of %s failed", getpid()); /*NOTREACHED*/ } /* reopen the log files so that they can be moved and removed */ close_system_logs(); open_system_logs(); /* recache all of the driver info, to get any changes */ #ifdef SIGCHLD (void) signal(SIGCHLD, SIG_DFL); #endif cache_directors(); cache_routers(); cache_transports(); #ifdef SIGCHLD (void) signal(SIGCHLD, daemon_sigchld); #endif /* do another queue run */ bg_run_queue(); (void) alarm(queue_interval); } } } /* * start_daemon - start a daemon smail process for noop_mode() or * daemon_mode() * * open system lots, get some system information we can use for * processing each message, and put ourselves in background. * * Return the pid of the child process in the parent, and 0 in the * child. */ static int start_daemon() { int pid; /* cache some interesting things */ open_system_logs(); if (primary_name == NULL) { build_host_strings(); } compute_nobody(); /* disconnect from the controlling terminal, if we are not debugging */ if (debug == 0) { pid = fork(); if (pid < 0) { write_log(LOG_TTY, "fork() failed: %s", strerrno()); exitvalue = EX_OSERR; return pid; } if (pid > 0) { /* in parent process, just exit */ return pid; } #ifdef POSIX_OS (void) setsid(); #else /* not POSIX_OS */ (void) setpgrp(0, getpid()); #endif /* POSIX_OS */ if (errfile) { (void) fclose(errfile); errfile = NULL; } if (isatty(fileno(stdout))) { (void) fclose(stdout); } if (isatty(fileno(stdin))) { (void) fclose(stdin); } } if (queue_interval > 0) { daemon_pid = getpid(); write_log(LOG_SYS, "pid %d: smail daemon started", daemon_pid); } /* toss the real uid under which smail was executed */ real_uid = getuid(); return 0; } /* * bg_run_queue - perform a queue run in a child process */ static void bg_run_queue() { int pid = fork(); if (pid == 0) { #ifdef POSIX_OS (void) setsid(); #else /* not POSIX_OS */ (void) setpgrp(0, getpid()); #endif /* POSIX_OS */ #ifdef SIGCHLD /* in child process we care about dying kids */ (void) signal(SIGCHLD, SIG_DFL); #endif (void) alarm(0); do_run_queue(); exit(0); } } /* * verify_addresses - print resolved addresses * * Get a list of addresses and return the output of resolve_addr_list() on * that list. */ void verify_addresses() { char *error; struct addr *cur; /* temp recipient addr list element */ struct addr *fail; /* list of failed addrs */ struct addr *defer; /* list of deferred addrs */ struct addr *deliver; /* addr structures ready to deliver */ struct addr **last_addr; /* pointer to current addr pointer */ X_PANIC_OKAY(); if (extract_addresses) { /* * read in the message from stdin, if the -t flag was set. */ input_signals(); /* prepare to remove message */ if (queue_message(stdin, dot_usage, recipients, &error) == FAIL) { if (errfile) { (void) fprintf(errfile, "%s: incoming message lost: %s: %s\n", program, error, strerrno()); } exitvalue = EX_OSFILE; return; } if (read_message() == NULL) { unlink_spool(); (void) fprintf(errfile, "failed to read queued message\n"); exitvalue = EX_OSFILE; return; } /* don't actually need the message anymore */ unlink_spool(); } if (primary_name == NULL) { /* setup all of the hostname information */ build_host_strings(); } /* figure out who nobody is */ compute_nobody(); /* * preparse all of the recipient addresses given as arguments. * If we are extracting addresses from the header, then * these addresses are NOT to receive the mail. To accomplish * this, add them to the hash table so they will be ignored * later. */ last_addr = &recipients; cur = recipients; while (cur) { char *error; /* error from preparse_address */ if ((cur->work_addr = preparse_address(cur->in_addr, &error)) == NULL) { if (errfile) { (void) fprintf(errfile, "%s ... syntax error in address: %s\n", cur->in_addr, error); } /* patch pointer to look at next address */ *last_addr = cur->succ; xfree((char *)cur); /* man page says I can still use cur */ cur = cur->succ; continue; } if (extract_addresses) { (void) add_to_hash(cur->work_addr, (char *)NULL, 0, hit_table); xfree(cur->work_addr); /* don't need it anymore */ xfree((char *)cur); cur = cur->succ; continue; } last_addr = &cur->succ; cur = cur->succ; } if (extract_addresses) { recipients = NULL; /* don't need them anymore */ /* * process_header will get the recipients from the header, * among other things we aren't really interested in here. */ error = process_header(&recipients); if (error && errfile) { (void) fprintf(errfile, "error in header: %s\n", error); } } /* * given the list of recipient addresses, turn those * addresses into more specific destinations, including * the transport that is to be used, in the case of * addresses destined remote */ deliver = NULL; defer = NULL; fail = NULL; resolve_addr_list(recipients, &deliver, &defer, &fail, TRUE); for (cur = deliver; cur; cur = cur->succ) { if (cur->next_host) { printf("%s at %s ... deliverable\n", cur->next_addr, cur->next_host); } else { printf("%s ... deliverable\n", cur->next_addr); } } for (cur = defer; cur; cur = cur->succ) { printf("%s ... error: %s\n", cur->in_addr, cur->error->message); } for (cur = fail; cur; cur = cur->succ) { printf("%s ... not deliverable: %s\n", cur->in_addr, cur->error->message); } close_system_logs(); } /* * do_run_queue - queue run assuming initial setup has been done */ static void do_run_queue() { char **work; /* vector of jobs */ DEBUG(DBG_MAIN_MID, "do_run_queue: called\n"); /* get work, and do it with child processes */ work = scan_spool_dirs(); while (*work) { if (errfile) { (void) fflush(errfile); } if (process_spool_file(*work) == FAIL) { /* fork failed, don't continue */ return; } /* message processed, go on to the next message */ work++; if (debug && errfile) { (void) putc('\n', errfile); } } DEBUG(DBG_MAIN_HI, "do_run_queue: finished\n"); } /* * print_version - display the current version string on stdout */ void print_version() { if (debug > 0) { puts(expand_string("\ release: Smail$version, $release_date\n\ patch number: #$patch_number, $patch_date\n\ compilation: #$compile_num, $compile_date")); } else { puts(version()); } } /* * print_copying_file - print the COPYING file, detailing distribution rights */ void print_copying_file() { register FILE *f; register int c; if (copying_file == NULL || (f = fopen(copying_file, "r")) == NULL) { (void) fprintf(stderr, "The file `%s' does not exist.\n\ Consult the file COPYING in the smail source directory for information\n\ on copying restrictions and warranty information from the authors\n", copying_file? copying_file: "COPYING"); exitvalue = EX_UNAVAILABLE; return; } while ((c = getc(f)) != EOF) { putchar(c); } (void) fclose(f); } /* * print_variables - write configuration variable values to stdout * * Names of variables are stored in the list of recipients. */ void print_variables() { register struct addr *cur; struct addr *new, *next; build_host_strings(); /* first reverse the list */ new = NULL; for (cur = recipients; cur; cur = next) { next = cur->succ; cur->succ = new; new = cur; } for (cur = new; cur; cur = cur->succ) { print_config_variable(cur->in_addr); } } /* * print_queue - list the current messages in the mail queue * * If debugging is enabled, print msglog associated with each message. */ void print_queue() { char **work; /* vector of jobs to process */ int col; /* current print column */ int save_debug = debug; debug = 0; X_PANIC_OKAY(); if (message_bufsiz > 4096) { message_bufsiz = 4096; /* don't need a big buffer */ } work = scan_spool_dirs(); while (*work) { char **argv; /* arguments from spool file */ char *error; /* open without locking */ if (open_spool(*work, FALSE, &error) == FAIL) { if (errfile) { if (errno == 0) { write_log(LOG_TTY, "%s/%s: %s", spool_dir, input_spool_fn, error); } else { write_log(LOG_TTY, "%s/%s: %s: %s", spool_dir, input_spool_fn, error, strerrno()); } } work++; continue; } sender = NULL; argv = read_message(); if (argv == NULL) { work++; continue; } (void) printf("%s\tFrom: %s (in %s/input)\n", message_id, sender, spool_dir); (void) printf("\t\tDate: %s\n", get_arpa_date(message_date())); /* * print the argument vectors several to a line, trying not to * go past the 76'th column */ if (*argv) { (void) printf("\t\tArgs: %s", *argv); col = 8 + 8 + sizeof("Args: ")-1 + strlen(*argv++); } while (*argv) { if (col + strlen(*argv) > 76) { col = 8 + 8 + sizeof("Args: ") - 1; (void) fputs("\n\t\t ", stdout); } else { putchar(' '); col++; } col += strlen(*argv); fputs(*argv++, stdout); } putchar('\n'); if (save_debug > 0) { send_log(stdout, TRUE, "Log of transactions:\n"); } close_spool(); work++; /* next assignment */ if (*work) { putchar('\n'); } } } /* * smtp_mode - receive and processes smtp transpactions * * Call receive_smtp() to get incoming messages. Then, if queue_only mode * is not set, deliver those messages. */ void smtp_mode(in, out) FILE *in; /* stream of SMTP commands */ FILE *out; /* channel for responses */ { open_system_logs(); if (primary_name == NULL) { build_host_strings(); } compute_nobody(); /* do the real work */ do_smtp(in, out); } /* * do_smtp - common routine used by smtp_mode() and daemon_mode() for SMTP * * NOTE: When receive_smtp is finished, in and out are closed. */ static void do_smtp(in, out) FILE *in; FILE *out; { char **files; /* files returned by receive_smtp() */ int cnt; /* count of files */ int i; /* cache some interesting things */ /* send out to process the SMTP transactions */ if (out) { X_PANIC_OKAY(); } else { X_NO_PANIC(); } files = receive_smtp(in, out); X_PANIC_OKAY(); (void) fclose(in); if (out) { (void) fclose(out); } /* if we are just queuing input, close and be done with it */ if (queue_only || deliver_mode == QUEUE_MESSAGE) { close_spool(); return; } for (cnt = 0; files[cnt] != NULL; cnt++) ; /* if delivering more than one mail message, cache driver info */ if (cnt > 1) { if (! cached_directors) { cache_directors(); } if (! cached_routers) { cache_routers(); } if (! cached_transports) { cache_transports(); } } /* * process the files last first (if the last file is still open) and * then first to the second to last This ordering is used because the * last one remains open and it requires less overhead if the last * file does not have to be reopened. */ for (cnt = 0; files[cnt] != NULL; cnt++) ; /* count the files */ if (spool_fn) { /* last file still open, finish processing it */ char **argv; /* args from read_message() */ int pid; /* pid of child process */ /* make a child process */ /* unlock the message in the parent process (see lock_message()) */ unlock_message(); pid = fork_wait(); if (pid < 0) { /* can't fork(), try again later for all messages */ if (errfile) { (void)fprintf(errfile, "%s: fork() failed: %s, try again later\n", program, strerrno()); (void)fflush(errfile); } return; } if (pid == 0) { /* in child process, process the message */ if (lock_message() == FAIL) { /* somebody else grabbed the lock, assume they will deliver */ exit(0); } argv = read_message(); if (argv == NULL) { exit(exitvalue); } /* process arguments from the spool file */ process_args(argv); /* perform delivery */ deliver_mail(); /* close the system-wide log files */ close_system_logs(); /* all done with the message */ exit(exitvalue); } /* * in the parent * * XXX - we need to close the open spool file, but going through * routines in spool.c would duplicate efforts already * done in the child process, so just close it ourselves. */ (void) close(spoolfile); --cnt; /* decrement the count */ } /* * process the remaining files */ for (i = 0; i < cnt; i++) { /* process_spool_file only returns FAIL on fork() failures */ if (process_spool_file(files[i]) == FAIL) { return ; } } } /* * process_spool_file - open read and process a spool file in a child process * * fork a child to open read and process an input spool file. Wait for * the child and return when the child has completed processing. * * Return FAIL if the fork() failed, otherwise return SUCCEED. */ int process_spool_file(spfn) char *spfn; /* spool file name */ { int pid = fork_wait(); char *error; if (pid < 0) { /* can't fork(), try again later */ if (errfile) { (void)fprintf(errfile, "%s: fork() failed: %s, try again later\n", program, strerrno()); (void)fflush(errfile); } return FAIL; } if (pid == 0) { /* in child process */ char **argv; /* arguments from spool file */ /* message grade is encoded in the last char of the filename */ msg_grade = spfn[strlen(spfn) - 1]; /* initialize state before reading state from the spool file */ initialize_state(); /* in child process, open the message and attempt delivery */ if (open_spool(spfn, TRUE, &error) == FAIL) { if (errno == 0) { write_log(LOG_SYS, "open_spool: %s/%s: %s", spool_dir, input_spool_fn, error); } else { write_log(LOG_SYS, "open_spool: %s/%s: %s: %s", spool_dir, input_spool_fn, error, strerrno()); } exit(EX_OSFILE); } argv = read_message(); if (argv == NULL) { panic(EX_OSFILE, "failed to read queued message in %s/%s", spool_dir, input_spool_fn); /*NOTREACHED*/ } /* process arguments from the spool file */ process_args(argv); /* perform delivery */ deliver_mail(); /* close the sytem-wide log files */ close_system_logs(); /* all done with the message */ exit(exitvalue); } return SUCCEED; } /* * fork_wait - fork and have the parent wait for the child to complete * * Return with 0 in the child process. * Return with -1 if fork() fails. * Return with the pid in the parent, though the wait() will already * have been done. */ int fork_wait() { int pid = fork(); int i; if (pid == 0) { return 0; } if (pid < 0) { return -1; } while ((i = wait((int *)0)) >= 0 && i != pid) ; return pid; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.