lmc@denelcor.UUCP (Lyle McElhaney) (03/28/85)
Greetings, I need to port sendmail from 4.2 onto an 11/44 running 2.9bsd. I am in the process of doing it now; I would like to know if anyone else has done it. At first glance, it doesn't seem to be trivial; running the make, the first lib module, syslog, asked for 3 includes involved in the 4.2 networking stuff. How hard is it to beat into submission? Any experience, useful hacks, and notes on how to manipulate the config files would be appreciated. One of the reasons for sendmail is that I send considerable batched mail out from here, and need the batched addressing feature. A question: How does the sending sendmail know when it is ok to batch? I assume it can only be done when the receiver is also sendmail; how can the sender know? Right now I mail from a sendmail machine through a single alias to over 200 people in a single message; senmail breaks it up and sends each message separately to our mail interface machine, which does not have sendmail. If I do get sendmail installed there, will it magically start batching (half of the mail is routed directly to ihnp4, for instance)? Thanks for any help. -- Lyle McElhaney {hao, stcvax, brl-bmd, nbires, csu-cs} !denelcor!lmc
satz@Shasta.ARPA (04/03/85)
I had a few problems bringing sendmail up on 2.9bsd when I did the port last year. There were three problems that stand out in my mind: 1) The timeout values needed to be declared and treated as longs. I think Berkeley has fixed this in the later versions of sendmail. 2) When the socket call fails on 2.9 (which happened quite regularly on the system I used), errno got trashed and the message would be returned instead of requeued. 3) daemon.c needed to be rewritten. Here is the daemon.c that I did. ------------------------------------------------------------------------ # include <errno.h> # include "sendmail.h" #ifndef DAEMON SCCSID(@(#)4.1a_daemon.c 3.35+ 12/5/82 (w/o daemon mode)); #else # include <sys/socket.h> # include <net/in.h> # include <netdb.h> # include <wait.h> SCCSID(@(#)4.1b_daemon.c 3.35+ 12/5/82 (with daemon mode)); /* ** DAEMON.C -- routines to use when running as a daemon. ** ** 4.1b BSD version -- this version is not well supported. ** ** This entire file is highly dependent on the 4.1b BSD ** interprocess communication primitives. No attempt has ** been made to make this file portable to Version 7, ** Version 6, MPX files, etc. If you should try such a ** thing yourself, I recommend chucking the entire file ** and starting from scratch. Basic semantics are: ** ** getrequests() ** Opens a port and initiates a connection. ** Returns in a child. Must set InChannel and ** OutChannel appropriately. ** clrdaemon() ** Close any open files associated with getting ** the connection; this is used when running the queue, ** etc., to avoid having extra file descriptors during ** the queue run and to avoid confusing the network ** code (if it cares). ** makeconnection(host, port, outfile, infile) ** Make a connection to the named host on the given ** port. Set *outfile and *infile to the files ** appropriate for communication. Returns zero on ** success, else an exit status describing the ** error. ** ** The semantics of both of these should be clean. */ /* ** GETREQUESTS -- open mail IPC port and get requests. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Waits until some interesting activity occurs. When ** it does, a child is created to process it, and the ** parent waits for completion. Return from this ** routine is always in the child. The file pointers ** "InChannel" and "OutChannel" should be set to point ** to the communication channel. */ struct sockaddr_in SendmailAddress;/* internet address of sendmail */ int DaemonSocket = -1; /* fd describing socket */ char *inet_ntoa(); getrequests() { int t; union wait status; register struct servent *sp; /* ** Set up the address for the mailer. */ sp = getservbyname("smtp", "tcp"); if (sp == NULL) { syserr("server \"smtp\" unknown"); # ifdef LOG if (LogLevel > 0) syslog(LOG_SALERT, "cannot get connection"); # endif LOG finis(); } SendmailAddress.sin_family = AF_INET; SendmailAddress.sin_addr.s_addr = INADDR_ANY; SendmailAddress.sin_port = sp->s_port; /* ** Try to actually open the connection */ for (;;) { register int pid; struct sockaddr_in otherend; extern int RefuseLA; # ifdef DEBUG if (tTd(15, 1)) printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); # endif DEBUG /* get a socket for the SMTP connection */ clrdaemon(); while ((DaemonSocket = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN)) < 0) { /* probably another daemon already */ syserr("getrequests: can't create socket"); sleep(30); } # ifdef DEBUG if (tTd(15, 1)) printf("getconnection: %d\n", DaemonSocket); # endif DEBUG /* see if we are rejecting connections */ while (getla() > RefuseLA) sleep(5); /* wait for a connection */ do { errno = 0; t = accept(DaemonSocket, &otherend); } while (t < 0 && errno == EINTR); if (t < 0) { syserr("getrequests: accept"); sleep(5); continue; } /* ** Create a subprocess to process the mail. */ # ifdef DEBUG if (tTd(15, 1)) printf("getrequests: forking (fd = %d)\n", DaemonSocket); # endif DEBUG pid = fork(); if (pid < 0) { syserr("daemon: cannot fork"); sleep(10); (void) close(DaemonSocket); continue; } if (pid == 0) { extern struct hostent *gethostbyaddr(); register struct hostent *hp; extern char *RealHostName; /* srvrsmtp.c */ char buf[MAXNAME]; /* ** CHILD -- return to caller. ** Collect verified idea of sending host. ** Verify calling user id if possible here. */ /* determine host name */ hp = gethostbyaddr(&otherend.sin_addr, sizeof otherend.sin_addr, AF_INET); if (hp != NULL) strcpy(buf, hp->h_name); else strcpy(buf, inet_ntoa(otherend.sin_addr)); RealHostName = newstr(buf); InChannel = fdopen(DaemonSocket, "r"); OutChannel = fdopen(DaemonSocket, "w"); # ifdef DEBUG if (tTd(15, 1)) printf("getreq: returning\n"); # endif DEBUG # ifdef LOG if (LogLevel > 11) syslog(LOG_DEBUG, "connected, pid=%d", getpid()); # endif LOG return; } /* ** PARENT -- wait for child to terminate. ** Perhaps we should allow concurrent processing? */ # ifdef DEBUG if (tTd(15, 1)) { sleep(2); printf("getreq: parent waiting\n"); } # endif DEBUG /* close the port so that others will hang (for a while) */ (void) close(DaemonSocket); /* pick up old zombies */ while (wait3(&status, WNOHANG, 0) > 0) continue; } /*NOTREACHED*/ } /* ** CLRDAEMON -- reset the daemon connection ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** releases any resources used by the passive daemon. */ clrdaemon() { if (tTd(15, 1)) printf("clrdaemon: %d\n", DaemonSocket); if (DaemonSocket >= 0) (void) close(DaemonSocket); DaemonSocket = -1; } /* ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. ** ** Parameters: ** host -- the name of the host. ** port -- the port number to connect to. ** outfile -- a pointer to a place to put the outfile ** descriptor. ** infile -- ditto for infile. ** ** Returns: ** An exit code telling whether the connection could be ** made and if not why not. ** ** Side Effects: ** none. */ makeconnection(host, port, outfile, infile) char *host; u_short port; FILE **outfile; FILE **infile; { register int s; /* ** Set up the address for the mailer. ** Accept "[a.b.c.d]" syntax for host name. */ if (host[0] == '[') { long hid; register char *p = index(host, ']'); if (p != NULL) { *p = '\0'; hid = inet_addr(&host[1]); *p = ']'; } if (p == NULL || hid == -1) { usrerr("Invalid numeric domain spec \"%s\"", host); return (EX_NOHOST); } SendmailAddress.sin_addr.s_addr = hid; } else { register struct hostent *hp = gethostbyname(host); if (hp == NULL) return (EX_NOHOST); bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); } /* ** Determine the port number. */ if (port != 0) SendmailAddress.sin_port = htons(port); else { register struct servent *sp = getservbyname("smtp", "tcp"); if (sp == NULL) { syserr("makeconnection: server \"smtp\" unknown"); return (EX_OSFILE); } SendmailAddress.sin_port = sp->s_port; } /* ** Try to actually open the connection. */ # ifdef DEBUG if (tTd(16, 1)) printf("makeconnection (%s %x)\n", host, SendmailAddress.sin_port); # endif DEBUG errno = 0; s = socket(SOCK_STREAM, 0, 0, 0); if (s < 0) { int saverr = errno; syserr("makeconnection: no socket"); errno = saverr; goto failure; } # ifdef DEBUG if (tTd(16, 1)) printf("makeconnection: %d\n", s); # endif DEBUG (void) fflush(CurEnv->e_xfp); /* for debugging */ errno = 0; /* for debugging */ SendmailAddress.sin_family = AF_INET; if (connect(s, &SendmailAddress) < 0) { /* failure, decide if temporary or not */ failure: #ifdef DEBUG if (tTd(16, 1)) printf("makeconnection failed: %d\n", errno); #endif switch (errno) { case EISCONN: case ETIMEDOUT: case EINPROGRESS: case EALREADY: case EADDRINUSE: case EHOSTDOWN: case ENETDOWN: case ENETRESET: case ENOBUFS: case ENOTCONN: case ECONNREFUSED: case ECONNRESET: case ECONNABORTED: case EHOSTUNREACH: case ENETUNREACH: /* there are others, I'm sure..... */ return (EX_TEMPFAIL); case EPERM: /* why is this happening? */ syserr("makeconnection: funny failure, addr=%lx, port=%x", SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port); return (EX_TEMPFAIL); default: return (EX_UNAVAILABLE); } } /* connection ok, put it into canonical form */ *outfile = fdopen(s, "w"); *infile = fdopen(s, "r"); return (EX_OK); } /* ** MYHOSTNAME -- return the name of this host. ** ** Parameters: ** hostbuf -- a place to return the name of this host. ** size -- the size of hostbuf. ** ** Returns: ** A list of aliases for this host. ** NULL if it can't find an alias list. ** ** Side Effects: ** none. */ char ** myhostname(hostbuf, size) char hostbuf[]; int size; { extern struct hostent *gethostbyname(); struct hostent *hp; gethostname(hostbuf, size); hp = gethostbyname(hostbuf); if (hp != NULL) { strncpy(hostbuf, hp->h_name, size); return (hp->h_aliases); } else return (NULL); } # else DAEMON /* ** MYHOSTNAME -- stub version for case of no daemon code. ** ** Can't convert to upper case here because might be a UUCP name. ** ** Mark, you can change this to be anything you want...... */ char ** myhostname(hostbuf, size) char hostbuf[]; int size; { register FILE *f; hostbuf[0] = '\0'; f = fopen("/usr/include/whoami", "r"); if (f != NULL) { (void) fgets(hostbuf, size, f); fixcrlf(hostbuf, TRUE); (void) fclose(f); } return (NULL); } #endif DAEMON