17aa493c5Seric # include <errno.h> 27fa39d90Seric # include "sendmail.h" 37fa39d90Seric 4d0a9e852Seric #ifndef DAEMON 5*877a6142Seric SCCSID(@(#)daemon.c 3.35 12/05/82 (w/o daemon mode)); 6d0a9e852Seric #else 7d0a9e852Seric 8d0a9e852Seric # include <sys/socket.h> 9d0a9e852Seric # include <net/in.h> 102554d49fSeric # include <wait.h> 11d0a9e852Seric 12*877a6142Seric SCCSID(@(#)daemon.c 3.35 12/05/82 (with daemon mode)); 137fa39d90Seric 147fa39d90Seric /* 157fa39d90Seric ** DAEMON.C -- routines to use when running as a daemon. 1647b12ae1Seric ** 1747b12ae1Seric ** This entire file is highly dependent on the 4.2 BSD 1847b12ae1Seric ** interprocess communication primitives. No attempt has 1947b12ae1Seric ** been made to make this file portable to Version 7, 2047b12ae1Seric ** Version 6, MPX files, etc. If you should try such a 2147b12ae1Seric ** thing yourself, I recommend chucking the entire file 2247b12ae1Seric ** and starting from scratch. Basic semantics are: 2347b12ae1Seric ** 2447b12ae1Seric ** getrequests() 2547b12ae1Seric ** Opens a port and initiates a connection. 2647b12ae1Seric ** Returns in a child. Must set InChannel and 2747b12ae1Seric ** OutChannel appropriately. 2847b12ae1Seric ** makeconnection(host, port, outfile, infile) 2947b12ae1Seric ** Make a connection to the named host on the given 3047b12ae1Seric ** port. Set *outfile and *infile to the files 3147b12ae1Seric ** appropriate for communication. Returns zero on 3247b12ae1Seric ** success, else an exit status describing the 3347b12ae1Seric ** error. 3447b12ae1Seric ** 3547b12ae1Seric ** The semantics of both of these should be clean. 367fa39d90Seric */ 377fa39d90Seric /* 387fa39d90Seric ** GETREQUESTS -- open mail IPC port and get requests. 397fa39d90Seric ** 407fa39d90Seric ** Parameters: 417fa39d90Seric ** none. 427fa39d90Seric ** 437fa39d90Seric ** Returns: 447fa39d90Seric ** none. 457fa39d90Seric ** 467fa39d90Seric ** Side Effects: 477fa39d90Seric ** Waits until some interesting activity occurs. When 487fa39d90Seric ** it does, a child is created to process it, and the 497fa39d90Seric ** parent waits for completion. Return from this 507fa39d90Seric ** routine is always in the child. 517fa39d90Seric */ 527fa39d90Seric 532554d49fSeric # define MAXCONNS 4 /* maximum simultaneous sendmails */ 542554d49fSeric 557fa39d90Seric getrequests() 567fa39d90Seric { 572554d49fSeric union wait status; 582554d49fSeric int numconnections = 0; 592554d49fSeric 60eb889047Seric for (;;) 61eb889047Seric { 62a8268164Seric register int pid; 63d0a9e852Seric register int port; 64eb889047Seric 65a8268164Seric /* 66a8268164Seric ** Wait for a connection. 67eb889047Seric */ 68eb889047Seric 69d0a9e852Seric while ((port = getconnection()) < 0) 70d0a9e852Seric { 71b0ba8827Seric # ifdef LOG 72b0ba8827Seric if (LogLevel > 0) 73b0ba8827Seric syslog(LOG_SALERT, "cannot get connection"); 74b0ba8827Seric # endif LOG 7547b12ae1Seric finis(); 76d0a9e852Seric } 77d0a9e852Seric 78d0a9e852Seric /* 79d0a9e852Seric ** Create a subprocess to process the mail. 80d0a9e852Seric */ 81d0a9e852Seric 82d0a9e852Seric # ifdef DEBUG 8361e4310fSeric if (tTd(15, 2)) 84d0a9e852Seric printf("getrequests: forking (port = %d)\n", port); 85d0a9e852Seric # endif DEBUG 86eb889047Seric 87a8268164Seric pid = fork(); 88a8268164Seric if (pid < 0) 89a8268164Seric { 90a8268164Seric syserr("daemon: cannot fork"); 91a8268164Seric sleep(10); 92b7db1904Seric (void) close(port); 93a8268164Seric continue; 94a8268164Seric } 95a8268164Seric 96a8268164Seric if (pid == 0) 97a8268164Seric { 98a8268164Seric /* 99a8268164Seric ** CHILD -- return to caller. 100a8268164Seric ** Verify calling user id if possible here. 101a8268164Seric */ 102a8268164Seric 103d0a9e852Seric InChannel = fdopen(port, "r"); 104d0a9e852Seric OutChannel = fdopen(port, "w"); 105d0a9e852Seric # ifdef DEBUG 10661e4310fSeric if (tTd(15, 2)) 107d0a9e852Seric printf("getreq: returning\n"); 108d0a9e852Seric # endif DEBUG 109252c8a22Seric # ifdef LOG 110252c8a22Seric if (LogLevel > 11) 111252c8a22Seric syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 112252c8a22Seric # endif LOG 113a8268164Seric return; 114a8268164Seric } 115a8268164Seric 116a8268164Seric /* 117a8268164Seric ** PARENT -- wait for child to terminate. 118a8268164Seric ** Perhaps we should allow concurrent processing? 119a8268164Seric */ 120a8268164Seric 121d0a9e852Seric # ifdef DEBUG 12261e4310fSeric if (tTd(15, 2)) 123d0a9e852Seric { 124d0a9e852Seric sleep(2); 125d0a9e852Seric printf("getreq: parent waiting\n"); 126d0a9e852Seric } 127d0a9e852Seric # endif DEBUG 128d0a9e852Seric 1292554d49fSeric /* close the port so that others will hang (for a while) */ 130b7db1904Seric (void) close(port); 1312554d49fSeric 1322554d49fSeric /* pick up old zombies; implement load limiting */ 1332554d49fSeric numconnections++; 1342554d49fSeric while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 1352554d49fSeric numconnections--; 136a8268164Seric } 1377fa39d90Seric } 138d0a9e852Seric /* 139d0a9e852Seric ** GETCONNECTION -- make a connection with the outside world 140d0a9e852Seric ** 1418aa272cfSeric ** This routine is horribly contorted to try to get around a bunch 1428aa272cfSeric ** of 4.1a IPC bugs. There appears to be nothing we can do to make 1438aa272cfSeric ** it "right" -- the code to interrupt accepts just doesn't work 1448aa272cfSeric ** right. However, this is an attempt to minimize the probablity 1458aa272cfSeric ** of problems. 1468aa272cfSeric ** 147d0a9e852Seric ** Parameters: 148d0a9e852Seric ** none. 149d0a9e852Seric ** 150d0a9e852Seric ** Returns: 151d0a9e852Seric ** The port for mail traffic. 152d0a9e852Seric ** 153d0a9e852Seric ** Side Effects: 154d0a9e852Seric ** Waits for a connection. 155d0a9e852Seric */ 156d0a9e852Seric 1572554d49fSeric #define IPPORT_PLAYPORT 3055 /* random number */ 1582554d49fSeric 159038c4785Seric struct sockaddr_in SendmailAddress = { AF_INET, IPPORT_SMTP }; 160d0a9e852Seric 161d0a9e852Seric getconnection() 162d0a9e852Seric { 16310d42158Seric int s; 16410d42158Seric #ifdef NVMUNIX 16510d42158Seric int t; 16610d42158Seric #endif NVMUNIX 167d0a9e852Seric struct sockaddr otherend; 168d0a9e852Seric 169d0a9e852Seric /* 170d0a9e852Seric ** Set up the address for the mailer. 171d0a9e852Seric */ 172d0a9e852Seric 17319cf7d27Seric SendmailAddress.sin_addr.s_addr = 0; 17448ff0a9dSeric SendmailAddress.sin_port = IPPORT_SMTP; 1752554d49fSeric # ifdef DEBUG 17661e4310fSeric if (tTd(15, 15)) 1772554d49fSeric SendmailAddress.sin_port = IPPORT_PLAYPORT; 1782554d49fSeric # endif DEBUG 1792554d49fSeric SendmailAddress.sin_port = htons(SendmailAddress.sin_port); 180d0a9e852Seric 181d0a9e852Seric /* 182d0a9e852Seric ** Try to actually open the connection. 183d0a9e852Seric */ 184d0a9e852Seric 185d0a9e852Seric # ifdef DEBUG 18661e4310fSeric if (tTd(15, 1)) 18719cf7d27Seric printf("getconnection\n"); 188d0a9e852Seric # endif DEBUG 189d0a9e852Seric 1908aa272cfSeric for (;; sleep(15)) 19147b12ae1Seric { 192d7a9bebcSeric int i; 193d7a9bebcSeric 19447b12ae1Seric /* get a socket for the SMTP connection */ 19510d42158Seric #ifdef NVMUNIX 19610d42158Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 19710d42158Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 19810d42158Seric listen(s, 10); 19910d42158Seric #else NVMUNIX 200d7a9bebcSeric /* do loop is to avoid 4.1b kernel bug (?) */ 2018aa272cfSeric i = 60; 202d7a9bebcSeric do 203d7a9bebcSeric { 204d0a9e852Seric s = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN); 2052554d49fSeric if (s < 0) 2068aa272cfSeric sleep(10); 207d7a9bebcSeric } while (--i > 0 && s < 0); 20810d42158Seric #endif NVMUNIX 209d7a9bebcSeric if (s < 0) 2102554d49fSeric { 21147b12ae1Seric /* probably another daemon already */ 21247b12ae1Seric syserr("getconnection: can't create socket"); 2130ef8369eSeric return (-1); 2142554d49fSeric } 215d0a9e852Seric 216d0a9e852Seric # ifdef DEBUG 21761e4310fSeric if (tTd(15, 1)) 218d0a9e852Seric printf("getconnection: %d\n", s); 219d0a9e852Seric # endif DEBUG 22047b12ae1Seric 22147b12ae1Seric /* wait for a connection */ 2228aa272cfSeric do 2238aa272cfSeric { 2240ef8369eSeric errno = 0; 22510d42158Seric #ifdef NVMUNIX 22610d42158Seric lotherend = sizeof otherend; 22710d42158Seric t = accept(s, &otherend, &lotherend, 0); 22810d42158Seric if (t >= 0) 22910d42158Seric return (t); 23010d42158Seric #else NVMUNIX 2319cc51a1bSeric if (accept(s, &otherend) >= 0) 2320ef8369eSeric return (s); 23310d42158Seric #endif NVMUNIX 2348aa272cfSeric } while (errno == EINTR); 235b7cb2f21Seric syserr("getconnection: accept"); 236b7cb2f21Seric sleep(5); 2378aa272cfSeric (void) close(s); 238b7cb2f21Seric } 2392554d49fSeric } 2407aa493c5Seric /* 2417aa493c5Seric ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 2427aa493c5Seric ** 2437aa493c5Seric ** Parameters: 2447aa493c5Seric ** host -- the name of the host. 24548ff0a9dSeric ** port -- the port number to connect to. 2467aa493c5Seric ** outfile -- a pointer to a place to put the outfile 2477aa493c5Seric ** descriptor. 2487aa493c5Seric ** infile -- ditto for infile. 2497aa493c5Seric ** 2507aa493c5Seric ** Returns: 2517aa493c5Seric ** An exit code telling whether the connection could be 2527aa493c5Seric ** made and if not why not. 2537aa493c5Seric ** 2547aa493c5Seric ** Side Effects: 2557aa493c5Seric ** none. 2567aa493c5Seric */ 2577aa493c5Seric 25848ff0a9dSeric makeconnection(host, port, outfile, infile) 2597aa493c5Seric char *host; 260210215eaSeric u_short port; 2617aa493c5Seric FILE **outfile; 2627aa493c5Seric FILE **infile; 2637aa493c5Seric { 2647aa493c5Seric register int s; 2657aa493c5Seric 2667aa493c5Seric /* 2677aa493c5Seric ** Set up the address for the mailer. 26871096d12Seric ** Accept "[a.b.c.d]" syntax for host name. 2697aa493c5Seric */ 2707aa493c5Seric 27171096d12Seric if (host[0] == '[') 27271096d12Seric { 27371096d12Seric long hid = 0; 27471096d12Seric int i, j; 27571096d12Seric register char *p = host; 27671096d12Seric 27771096d12Seric for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 27871096d12Seric { 27971096d12Seric j = 0; 28071096d12Seric while (isdigit(*++p)) 28171096d12Seric j = j * 10 + (*p - '0'); 28271096d12Seric if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 28371096d12Seric break; 28471096d12Seric hid |= j << ((3 - i) * 8); 28571096d12Seric } 28671096d12Seric if (i >= 0 || *p != ']' || *++p != '\0') 28771096d12Seric { 28871096d12Seric usrerr("Invalid numeric domain spec \"%s\"", host); 28971096d12Seric return (EX_NOHOST); 29071096d12Seric } 29171096d12Seric SendmailAddress.sin_addr.s_addr = hid; 29271096d12Seric } 29371096d12Seric else if ((SendmailAddress.sin_addr.s_addr = rhost(&host)) == -1) 2947aa493c5Seric return (EX_NOHOST); 29548ff0a9dSeric if (port == 0) 29648ff0a9dSeric port = IPPORT_SMTP; 2972554d49fSeric SendmailAddress.sin_port = htons(port); 2987aa493c5Seric 2997aa493c5Seric /* 3007aa493c5Seric ** Try to actually open the connection. 3017aa493c5Seric */ 3027aa493c5Seric 3037aa493c5Seric # ifdef DEBUG 30461e4310fSeric if (tTd(16, 1)) 3057aa493c5Seric printf("makeconnection (%s)\n", host); 3067aa493c5Seric # endif DEBUG 3077aa493c5Seric 30810d42158Seric #ifdef NVMUNIX 30910d42158Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 31010d42158Seric #else NVMUNIX 311b7db1904Seric s = socket(SOCK_STREAM, 0, (struct sockaddr_in *) 0, 0); 31210d42158Seric #endif NVMUNIX 3137aa493c5Seric if (s < 0) 3147aa493c5Seric { 3157aa493c5Seric syserr("makeconnection: no socket"); 3167aa493c5Seric goto failure; 3177aa493c5Seric } 3187aa493c5Seric 3197aa493c5Seric # ifdef DEBUG 32061e4310fSeric if (tTd(16, 1)) 3217aa493c5Seric printf("makeconnection: %d\n", s); 3227aa493c5Seric # endif DEBUG 323*877a6142Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 32410d42158Seric #ifdef NVMUNIX 32510d42158Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 32610d42158Seric if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 32710d42158Seric #else NVMUNIX 3287aa493c5Seric if (connect(s, &SendmailAddress) < 0) 32910d42158Seric #endif NVMUNIX 3307aa493c5Seric { 3317aa493c5Seric /* failure, decide if temporary or not */ 3327aa493c5Seric failure: 3337aa493c5Seric switch (errno) 3347aa493c5Seric { 3357aa493c5Seric case EISCONN: 3367aa493c5Seric case ETIMEDOUT: 337292668b9Seric case EINPROGRESS: 338292668b9Seric case EALREADY: 339292668b9Seric case EADDRINUSE: 340292668b9Seric case ENETDOWN: 341292668b9Seric case ENETRESET: 342292668b9Seric case ENOBUFS: 343a1645df8Seric case ECONNREFUSED: 3447aa493c5Seric /* there are others, I'm sure..... */ 3457aa493c5Seric return (EX_TEMPFAIL); 3467aa493c5Seric 3477aa493c5Seric default: 3487aa493c5Seric return (EX_UNAVAILABLE); 3497aa493c5Seric } 3507aa493c5Seric } 3517aa493c5Seric 3527aa493c5Seric /* connection ok, put it into canonical form */ 3537aa493c5Seric *outfile = fdopen(s, "w"); 3547aa493c5Seric *infile = fdopen(s, "r"); 3557aa493c5Seric 3567aa493c5Seric return (0); 3577aa493c5Seric } 358d0a9e852Seric 359d0a9e852Seric #endif DAEMON 360