16039Seric # include <errno.h> 24535Seric # include "sendmail.h" 34535Seric 45978Seric #ifndef DAEMON 5*9310Seric SCCSID(@(#)daemon.c 3.34 11/21/82 (w/o daemon mode)); 65978Seric #else 74535Seric 85978Seric # include <sys/socket.h> 95978Seric # include <net/in.h> 107117Seric # include <wait.h> 115978Seric 12*9310Seric SCCSID(@(#)daemon.c 3.34 11/21/82 (with daemon mode)); 135978Seric 144535Seric /* 154535Seric ** DAEMON.C -- routines to use when running as a daemon. 167556Seric ** 177556Seric ** This entire file is highly dependent on the 4.2 BSD 187556Seric ** interprocess communication primitives. No attempt has 197556Seric ** been made to make this file portable to Version 7, 207556Seric ** Version 6, MPX files, etc. If you should try such a 217556Seric ** thing yourself, I recommend chucking the entire file 227556Seric ** and starting from scratch. Basic semantics are: 237556Seric ** 247556Seric ** getrequests() 257556Seric ** Opens a port and initiates a connection. 267556Seric ** Returns in a child. Must set InChannel and 277556Seric ** OutChannel appropriately. 287556Seric ** makeconnection(host, port, outfile, infile) 297556Seric ** Make a connection to the named host on the given 307556Seric ** port. Set *outfile and *infile to the files 317556Seric ** appropriate for communication. Returns zero on 327556Seric ** success, else an exit status describing the 337556Seric ** error. 347556Seric ** 357556Seric ** The semantics of both of these should be clean. 364535Seric */ 374535Seric /* 384535Seric ** GETREQUESTS -- open mail IPC port and get requests. 394535Seric ** 404535Seric ** Parameters: 414535Seric ** none. 424535Seric ** 434535Seric ** Returns: 444535Seric ** none. 454535Seric ** 464535Seric ** Side Effects: 474535Seric ** Waits until some interesting activity occurs. When 484535Seric ** it does, a child is created to process it, and the 494535Seric ** parent waits for completion. Return from this 504535Seric ** routine is always in the child. 514535Seric */ 524535Seric 537117Seric # define MAXCONNS 4 /* maximum simultaneous sendmails */ 547117Seric 554535Seric getrequests() 564535Seric { 577117Seric union wait status; 587117Seric int numconnections = 0; 597117Seric 604631Seric for (;;) 614631Seric { 624636Seric register int pid; 635978Seric register int port; 644631Seric 654636Seric /* 664636Seric ** Wait for a connection. 674636Seric */ 684631Seric 695978Seric while ((port = getconnection()) < 0) 705978Seric { 718518Seric # ifdef LOG 728518Seric if (LogLevel > 0) 738518Seric syslog(LOG_SALERT, "cannot get connection"); 748518Seric # endif LOG 757556Seric finis(); 765978Seric } 774631Seric 785978Seric /* 795978Seric ** Create a subprocess to process the mail. 805978Seric */ 815978Seric 825978Seric # ifdef DEBUG 837677Seric if (tTd(15, 2)) 845978Seric printf("getrequests: forking (port = %d)\n", port); 855978Seric # endif DEBUG 865978Seric 874636Seric pid = fork(); 884636Seric if (pid < 0) 894631Seric { 904636Seric syserr("daemon: cannot fork"); 914636Seric sleep(10); 927009Seric (void) close(port); 934636Seric continue; 944631Seric } 954631Seric 964636Seric if (pid == 0) 974631Seric { 984636Seric /* 994636Seric ** CHILD -- return to caller. 1004636Seric ** Verify calling user id if possible here. 1014636Seric */ 1024631Seric 1035978Seric InChannel = fdopen(port, "r"); 1045978Seric OutChannel = fdopen(port, "w"); 1055978Seric # ifdef DEBUG 1067677Seric if (tTd(15, 2)) 1075978Seric printf("getreq: returning\n"); 1085978Seric # endif DEBUG 1097876Seric # ifdef LOG 1107876Seric if (LogLevel > 11) 1117876Seric syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 1127876Seric # endif LOG 1134636Seric return; 1144631Seric } 1154631Seric 1164636Seric /* 1174636Seric ** PARENT -- wait for child to terminate. 1184636Seric ** Perhaps we should allow concurrent processing? 1194636Seric */ 1204631Seric 1215978Seric # ifdef DEBUG 1227677Seric if (tTd(15, 2)) 1235978Seric { 1245978Seric sleep(2); 1255978Seric printf("getreq: parent waiting\n"); 1265978Seric } 1275978Seric # endif DEBUG 1285978Seric 1297117Seric /* close the port so that others will hang (for a while) */ 1307009Seric (void) close(port); 1317117Seric 1327117Seric /* pick up old zombies; implement load limiting */ 1337117Seric numconnections++; 1347117Seric while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 1357117Seric numconnections--; 1364631Seric } 1374631Seric } 1385978Seric /* 1395978Seric ** GETCONNECTION -- make a connection with the outside world 1405978Seric ** 1418822Seric ** This routine is horribly contorted to try to get around a bunch 1428822Seric ** of 4.1a IPC bugs. There appears to be nothing we can do to make 1438822Seric ** it "right" -- the code to interrupt accepts just doesn't work 1448822Seric ** right. However, this is an attempt to minimize the probablity 1458822Seric ** of problems. 1468822Seric ** 1475978Seric ** Parameters: 1485978Seric ** none. 1495978Seric ** 1505978Seric ** Returns: 1515978Seric ** The port for mail traffic. 1525978Seric ** 1535978Seric ** Side Effects: 1545978Seric ** Waits for a connection. 1555978Seric */ 1565978Seric 1577117Seric #define IPPORT_PLAYPORT 3055 /* random number */ 1587117Seric 1595979Seric struct sockaddr_in SendmailAddress = { AF_INET, IPPORT_SMTP }; 1605978Seric 1615978Seric getconnection() 1625978Seric { 163*9310Seric int s; 164*9310Seric #ifdef NVMUNIX 165*9310Seric int t; 166*9310Seric #endif NVMUNIX 1675978Seric struct sockaddr otherend; 1685978Seric 1695978Seric /* 1705978Seric ** Set up the address for the mailer. 1715978Seric */ 1725978Seric 1736058Seric SendmailAddress.sin_addr.s_addr = 0; 1746633Seric SendmailAddress.sin_port = IPPORT_SMTP; 1757117Seric # ifdef DEBUG 1767677Seric if (tTd(15, 15)) 1777117Seric SendmailAddress.sin_port = IPPORT_PLAYPORT; 1787117Seric # endif DEBUG 1797117Seric SendmailAddress.sin_port = htons(SendmailAddress.sin_port); 1805978Seric 1815978Seric /* 1825978Seric ** Try to actually open the connection. 1835978Seric */ 1845978Seric 1855978Seric # ifdef DEBUG 1867677Seric if (tTd(15, 1)) 1876058Seric printf("getconnection\n"); 1885978Seric # endif DEBUG 1895978Seric 1908822Seric for (;; sleep(15)) 1917117Seric { 1928352Seric int i; 1938352Seric 1947556Seric /* get a socket for the SMTP connection */ 195*9310Seric #ifdef NVMUNIX 196*9310Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 197*9310Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 198*9310Seric listen(s, 10); 199*9310Seric #else NVMUNIX 2008352Seric /* do loop is to avoid 4.1b kernel bug (?) */ 2018822Seric i = 60; 2028352Seric do 2038352Seric { 2048352Seric s = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN); 2058352Seric if (s < 0) 2068822Seric sleep(10); 2078352Seric } while (--i > 0 && s < 0); 208*9310Seric #endif NVMUNIX 2097556Seric if (s < 0) 2107556Seric { 2117556Seric /* probably another daemon already */ 2127556Seric syserr("getconnection: can't create socket"); 2137892Seric return (-1); 2147556Seric } 2155978Seric 2165978Seric # ifdef DEBUG 2177677Seric if (tTd(15, 1)) 2187556Seric printf("getconnection: %d\n", s); 2195978Seric # endif DEBUG 2207556Seric 2217556Seric /* wait for a connection */ 2228822Seric do 2238822Seric { 2248822Seric errno = 0; 225*9310Seric #ifdef NVMUNIX 226*9310Seric lotherend = sizeof otherend; 227*9310Seric t = accept(s, &otherend, &lotherend, 0); 228*9310Seric if (t >= 0) 229*9310Seric return (t); 230*9310Seric #else NVMUNIX 2318822Seric if (accept(s, &otherend) >= 0) 2328822Seric return (s); 233*9310Seric #endif NVMUNIX 2348822Seric } while (errno == EINTR); 2358822Seric syserr("getconnection: accept"); 2368822Seric sleep(5); 2378276Seric (void) close(s); 2387117Seric } 2395978Seric } 2406039Seric /* 2416039Seric ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 2426039Seric ** 2436039Seric ** Parameters: 2446039Seric ** host -- the name of the host. 2456633Seric ** port -- the port number to connect to. 2466039Seric ** outfile -- a pointer to a place to put the outfile 2476039Seric ** descriptor. 2486039Seric ** infile -- ditto for infile. 2496039Seric ** 2506039Seric ** Returns: 2516039Seric ** An exit code telling whether the connection could be 2526039Seric ** made and if not why not. 2536039Seric ** 2546039Seric ** Side Effects: 2556039Seric ** none. 2566039Seric */ 2575978Seric 2586633Seric makeconnection(host, port, outfile, infile) 2596039Seric char *host; 2607286Seric u_short port; 2616039Seric FILE **outfile; 2626039Seric FILE **infile; 2636039Seric { 2646039Seric register int s; 2656039Seric 2666039Seric /* 2676039Seric ** Set up the address for the mailer. 2689308Seric ** Accept "[a.b.c.d]" syntax for host name. 2696039Seric */ 2706039Seric 2719308Seric if (host[0] == '[') 2729308Seric { 2739308Seric long hid = 0; 2749308Seric int i, j; 2759308Seric register char *p = host; 2769308Seric 2779308Seric for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 2789308Seric { 2799308Seric j = 0; 2809308Seric while (isdigit(*++p)) 2819308Seric j = j * 10 + (*p - '0'); 2829308Seric if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 2839308Seric break; 2849308Seric hid |= j << ((3 - i) * 8); 2859308Seric } 2869308Seric if (i >= 0 || *p != ']' || *++p != '\0') 2879308Seric { 2889308Seric usrerr("Invalid numeric domain spec \"%s\"", host); 2899308Seric return (EX_NOHOST); 2909308Seric } 2919308Seric SendmailAddress.sin_addr.s_addr = hid; 2929308Seric } 2939308Seric else if ((SendmailAddress.sin_addr.s_addr = rhost(&host)) == -1) 2946039Seric return (EX_NOHOST); 2956633Seric if (port == 0) 2966633Seric port = IPPORT_SMTP; 2977117Seric SendmailAddress.sin_port = htons(port); 2986039Seric 2996039Seric /* 3006039Seric ** Try to actually open the connection. 3016039Seric */ 3026039Seric 3036039Seric # ifdef DEBUG 3047677Seric if (tTd(16, 1)) 3056039Seric printf("makeconnection (%s)\n", host); 3066039Seric # endif DEBUG 3076039Seric 308*9310Seric #ifdef NVMUNIX 309*9310Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 310*9310Seric #else NVMUNIX 3117009Seric s = socket(SOCK_STREAM, 0, (struct sockaddr_in *) 0, 0); 312*9310Seric #endif NVMUNIX 3136039Seric if (s < 0) 3146039Seric { 3156039Seric syserr("makeconnection: no socket"); 3166039Seric goto failure; 3176039Seric } 3186039Seric 3196039Seric # ifdef DEBUG 3207677Seric if (tTd(16, 1)) 3216039Seric printf("makeconnection: %d\n", s); 3226039Seric # endif DEBUG 3237677Seric (void) fflush(Xscript); /* for debugging */ 324*9310Seric #ifdef NVMUNIX 325*9310Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 326*9310Seric if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 327*9310Seric #else NVMUNIX 3286039Seric if (connect(s, &SendmailAddress) < 0) 329*9310Seric #endif NVMUNIX 3306039Seric { 3316039Seric /* failure, decide if temporary or not */ 3326039Seric failure: 3336039Seric switch (errno) 3346039Seric { 3356039Seric case EISCONN: 3366039Seric case ETIMEDOUT: 3376897Seric case EINPROGRESS: 3386897Seric case EALREADY: 3396897Seric case EADDRINUSE: 3406897Seric case ENETDOWN: 3416897Seric case ENETRESET: 3426897Seric case ENOBUFS: 3437204Seric case ECONNREFUSED: 3446039Seric /* there are others, I'm sure..... */ 3456039Seric return (EX_TEMPFAIL); 3466039Seric 3476039Seric default: 3486039Seric return (EX_UNAVAILABLE); 3496039Seric } 3506039Seric } 3516039Seric 3526039Seric /* connection ok, put it into canonical form */ 3536039Seric *outfile = fdopen(s, "w"); 3546039Seric *infile = fdopen(s, "r"); 3556039Seric 3566039Seric return (0); 3576039Seric } 3586039Seric 3595978Seric #endif DAEMON 360