16039Seric # include <errno.h> 24535Seric # include "sendmail.h" 34535Seric 45978Seric #ifndef DAEMON 5*10206Seric SCCSID(@(#)daemon.c 3.44 01/08/83 (w/o daemon mode)); 65978Seric #else 74535Seric 89610Seric #include <sys/socket.h> 99610Seric #include <netinet/in.h> 109610Seric #include <netdb.h> 119610Seric #include <wait.h> 125978Seric 13*10206Seric SCCSID(@(#)daemon.c 3.44 01/08/83 (with daemon mode)); 145978Seric 154535Seric /* 164535Seric ** DAEMON.C -- routines to use when running as a daemon. 177556Seric ** 187556Seric ** This entire file is highly dependent on the 4.2 BSD 197556Seric ** interprocess communication primitives. No attempt has 207556Seric ** been made to make this file portable to Version 7, 217556Seric ** Version 6, MPX files, etc. If you should try such a 227556Seric ** thing yourself, I recommend chucking the entire file 237556Seric ** and starting from scratch. Basic semantics are: 247556Seric ** 257556Seric ** getrequests() 267556Seric ** Opens a port and initiates a connection. 277556Seric ** Returns in a child. Must set InChannel and 287556Seric ** OutChannel appropriately. 29*10206Seric ** clrdaemon() 30*10206Seric ** Close any open files associated with getting 31*10206Seric ** the connection; this is used when running the queue, 32*10206Seric ** etc., to avoid having extra file descriptors during 33*10206Seric ** the queue run and to avoid confusing the network 34*10206Seric ** code (if it cares). 357556Seric ** makeconnection(host, port, outfile, infile) 367556Seric ** Make a connection to the named host on the given 377556Seric ** port. Set *outfile and *infile to the files 387556Seric ** appropriate for communication. Returns zero on 397556Seric ** success, else an exit status describing the 407556Seric ** error. 417556Seric ** 427556Seric ** The semantics of both of these should be clean. 434535Seric */ 444535Seric /* 454535Seric ** GETREQUESTS -- open mail IPC port and get requests. 464535Seric ** 474535Seric ** Parameters: 484535Seric ** none. 494535Seric ** 504535Seric ** Returns: 514535Seric ** none. 524535Seric ** 534535Seric ** Side Effects: 544535Seric ** Waits until some interesting activity occurs. When 554535Seric ** it does, a child is created to process it, and the 564535Seric ** parent waits for completion. Return from this 579886Seric ** routine is always in the child. The file pointers 589886Seric ** "InChannel" and "OutChannel" should be set to point 599886Seric ** to the communication channel. 604535Seric */ 614535Seric 627117Seric # define MAXCONNS 4 /* maximum simultaneous sendmails */ 637117Seric 64*10206Seric struct sockaddr_in SendmailAddress;/* internet address of sendmail */ 65*10206Seric int DaemonSocket = -1; /* fd describing socket */ 669610Seric 674535Seric getrequests() 684535Seric { 699610Seric int t; 707117Seric union wait status; 717117Seric int numconnections = 0; 729610Seric register struct servent *sp; 737117Seric 749610Seric /* 759610Seric ** Set up the address for the mailer. 769610Seric */ 779610Seric 789610Seric sp = getservbyname("smtp", "tcp"); 799610Seric if (sp == NULL) 809610Seric { 819610Seric syserr("server \"smtp\" unknown"); 8210167Seric goto severe; 839610Seric } 849610Seric SendmailAddress.sin_family = AF_INET; 859610Seric SendmailAddress.sin_addr.s_addr = INADDR_ANY; 869740Ssam SendmailAddress.sin_port = sp->s_port; 879610Seric 889610Seric /* 899610Seric ** Try to actually open the connection. 909610Seric */ 919610Seric 929610Seric # ifdef DEBUG 939610Seric if (tTd(15, 1)) 949610Seric printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 959610Seric # endif DEBUG 969610Seric 979610Seric /* get a socket for the SMTP connection */ 98*10206Seric DaemonSocket = socket(AF_INET, SOCK_STREAM, 0, 0); 99*10206Seric if (DaemonSocket < 0) 1009610Seric { 1019610Seric /* probably another daemon already */ 1029610Seric syserr("getrequests: can't create socket"); 1039610Seric severe: 1049610Seric # ifdef LOG 1059610Seric if (LogLevel > 0) 1069610Seric syslog(LOG_SALERT, "cannot get connection"); 1079610Seric # endif LOG 1089610Seric finis(); 1099610Seric } 110*10206Seric if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 1119610Seric { 1129610Seric syserr("getrequests: cannot bind"); 113*10206Seric (void) close(DaemonSocket); 1149610Seric goto severe; 1159610Seric } 116*10206Seric listen(DaemonSocket, 10); 1179610Seric 1189610Seric # ifdef DEBUG 1199610Seric if (tTd(15, 1)) 120*10206Seric printf("getrequests: %d\n", DaemonSocket); 1219610Seric # endif DEBUG 1229610Seric 1234631Seric for (;;) 1244631Seric { 1259610Seric /* wait for a connection */ 1264636Seric register int pid; 1274631Seric 1289610Seric do 1299610Seric { 1309610Seric auto int lotherend; 1319610Seric struct sockaddr otherend; 1324631Seric 1339610Seric errno = 0; 1349610Seric lotherend = sizeof otherend; 135*10206Seric t = accept(DaemonSocket, &otherend, &lotherend, 0); 1369610Seric } while (t < 0 && errno == EINTR); 1379610Seric if (t < 0) 1385978Seric { 1399610Seric syserr("getrequests: accept"); 1409610Seric sleep(5); 1419610Seric continue; 1425978Seric } 1434631Seric 1445978Seric /* 1455978Seric ** Create a subprocess to process the mail. 1465978Seric */ 1475978Seric 1485978Seric # ifdef DEBUG 1497677Seric if (tTd(15, 2)) 1509610Seric printf("getrequests: forking (fd = %d)\n", t); 1515978Seric # endif DEBUG 1525978Seric 1534636Seric pid = fork(); 1544636Seric if (pid < 0) 1554631Seric { 1564636Seric syserr("daemon: cannot fork"); 1574636Seric sleep(10); 1589610Seric (void) close(t); 1594636Seric continue; 1604631Seric } 1614631Seric 1624636Seric if (pid == 0) 1634631Seric { 1644636Seric /* 1654636Seric ** CHILD -- return to caller. 1664636Seric ** Verify calling user id if possible here. 1674636Seric */ 1684631Seric 169*10206Seric (void) close(DaemonSocket); 1709610Seric InChannel = fdopen(t, "r"); 1719610Seric OutChannel = fdopen(t, "w"); 1725978Seric # ifdef DEBUG 1737677Seric if (tTd(15, 2)) 1745978Seric printf("getreq: returning\n"); 1755978Seric # endif DEBUG 1767876Seric # ifdef LOG 1777876Seric if (LogLevel > 11) 1787876Seric syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 1797876Seric # endif LOG 1804636Seric return; 1814631Seric } 1824631Seric 1834636Seric /* 1844636Seric ** PARENT -- wait for child to terminate. 1854636Seric ** Perhaps we should allow concurrent processing? 1864636Seric */ 1874631Seric 1885978Seric # ifdef DEBUG 1897677Seric if (tTd(15, 2)) 1905978Seric { 1915978Seric sleep(2); 1925978Seric printf("getreq: parent waiting\n"); 1935978Seric } 1945978Seric # endif DEBUG 1955978Seric 1967117Seric /* close the port so that others will hang (for a while) */ 1979610Seric (void) close(t); 1987117Seric 1997117Seric /* pick up old zombies; implement load limiting */ 2007117Seric numconnections++; 2017117Seric while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 2027117Seric numconnections--; 2034631Seric } 2049886Seric /*NOTREACHED*/ 2054631Seric } 2065978Seric /* 207*10206Seric ** CLRDAEMON -- reset the daemon connection 208*10206Seric ** 209*10206Seric ** Parameters: 210*10206Seric ** none. 211*10206Seric ** 212*10206Seric ** Returns: 213*10206Seric ** none. 214*10206Seric ** 215*10206Seric ** Side Effects: 216*10206Seric ** releases any resources used by the passive daemon. 217*10206Seric */ 218*10206Seric 219*10206Seric clrdaemon() 220*10206Seric { 221*10206Seric if (DaemonSocket >= 0) 222*10206Seric (void) close(DaemonSocket); 223*10206Seric DaemonSocket = -1; 224*10206Seric } 225*10206Seric /* 2266039Seric ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 2276039Seric ** 2286039Seric ** Parameters: 2296039Seric ** host -- the name of the host. 2306633Seric ** port -- the port number to connect to. 2316039Seric ** outfile -- a pointer to a place to put the outfile 2326039Seric ** descriptor. 2336039Seric ** infile -- ditto for infile. 2346039Seric ** 2356039Seric ** Returns: 2366039Seric ** An exit code telling whether the connection could be 2376039Seric ** made and if not why not. 2386039Seric ** 2396039Seric ** Side Effects: 2406039Seric ** none. 2416039Seric */ 2425978Seric 2436633Seric makeconnection(host, port, outfile, infile) 2446039Seric char *host; 2457286Seric u_short port; 2466039Seric FILE **outfile; 2476039Seric FILE **infile; 2486039Seric { 2496039Seric register int s; 2506039Seric 2516039Seric /* 2526039Seric ** Set up the address for the mailer. 2539308Seric ** Accept "[a.b.c.d]" syntax for host name. 2546039Seric */ 2556039Seric 2569308Seric if (host[0] == '[') 2579308Seric { 2589308Seric long hid = 0; 2599308Seric int i, j; 2609308Seric register char *p = host; 2619308Seric 2629308Seric for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 2639308Seric { 2649308Seric j = 0; 2659308Seric while (isdigit(*++p)) 2669308Seric j = j * 10 + (*p - '0'); 2679308Seric if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 2689308Seric break; 2699308Seric hid |= j << ((3 - i) * 8); 2709308Seric } 2719308Seric if (i >= 0 || *p != ']' || *++p != '\0') 2729308Seric { 2739308Seric usrerr("Invalid numeric domain spec \"%s\"", host); 2749308Seric return (EX_NOHOST); 2759308Seric } 2769308Seric SendmailAddress.sin_addr.s_addr = hid; 2779308Seric } 2789610Seric else 2799610Seric { 2809610Seric register struct hostent *hp = gethostbyname(host); 2819610Seric 2829610Seric if (hp == 0) 2839610Seric return (EX_NOHOST); 2849610Seric bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 2859610Seric } 2869610Seric 2879610Seric /* 2889610Seric ** Determine the port number. 2899610Seric */ 2909610Seric 29110011Seric if (port != 0) 29210011Seric SendmailAddress.sin_port = htons(port); 29310011Seric else 2949610Seric { 2959610Seric register struct servent *sp = getservbyname("smtp", "tcp"); 2969610Seric 2979610Seric if (sp == NULL) 2989610Seric { 2999610Seric syserr("makeconnection: server \"smtp\" unknown"); 3009610Seric return (EX_OSFILE); 3019610Seric } 30210011Seric SendmailAddress.sin_port = sp->s_port; 3039610Seric } 3046039Seric 3056039Seric /* 3066039Seric ** Try to actually open the connection. 3076039Seric */ 3086039Seric 3096039Seric # ifdef DEBUG 3107677Seric if (tTd(16, 1)) 3116039Seric printf("makeconnection (%s)\n", host); 3126039Seric # endif DEBUG 3136039Seric 3149310Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 3156039Seric if (s < 0) 3166039Seric { 3176039Seric syserr("makeconnection: no socket"); 3186039Seric goto failure; 3196039Seric } 3206039Seric 3216039Seric # ifdef DEBUG 3227677Seric if (tTd(16, 1)) 3236039Seric printf("makeconnection: %d\n", s); 3246039Seric # endif DEBUG 3259546Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3269610Seric SendmailAddress.sin_family = AF_INET; 3279310Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 3289310Seric if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 3296039Seric { 3306039Seric /* failure, decide if temporary or not */ 3316039Seric failure: 3326039Seric switch (errno) 3336039Seric { 3346039Seric case EISCONN: 3356039Seric case ETIMEDOUT: 3366897Seric case EINPROGRESS: 3376897Seric case EALREADY: 3386897Seric case EADDRINUSE: 33910123Seric case EHOSTDOWN: 3406897Seric case ENETDOWN: 3416897Seric case ENETRESET: 3426897Seric case ENOBUFS: 3437204Seric case ECONNREFUSED: 34410081Seric case EHOSTUNREACH: 34510098Seric case ENETUNREACH: 3466039Seric /* there are others, I'm sure..... */ 3476039Seric return (EX_TEMPFAIL); 3486039Seric 3496039Seric default: 3506039Seric return (EX_UNAVAILABLE); 3516039Seric } 3526039Seric } 3536039Seric 3546039Seric /* connection ok, put it into canonical form */ 3556039Seric *outfile = fdopen(s, "w"); 3566039Seric *infile = fdopen(s, "r"); 3576039Seric 35810098Seric return (EX_OK); 3596039Seric } 3606039Seric 3615978Seric #endif DAEMON 362