16039Seric # include <errno.h> 24535Seric # include "sendmail.h" 34535Seric 45978Seric #ifndef DAEMON 5*10167Seric SCCSID(@(#)daemon.c 3.43 01/06/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*10167Seric SCCSID(@(#)daemon.c 3.43 01/06/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. 297556Seric ** makeconnection(host, port, outfile, infile) 307556Seric ** Make a connection to the named host on the given 317556Seric ** port. Set *outfile and *infile to the files 327556Seric ** appropriate for communication. Returns zero on 337556Seric ** success, else an exit status describing the 347556Seric ** error. 357556Seric ** 367556Seric ** The semantics of both of these should be clean. 374535Seric */ 384535Seric /* 394535Seric ** GETREQUESTS -- open mail IPC port and get requests. 404535Seric ** 414535Seric ** Parameters: 424535Seric ** none. 434535Seric ** 444535Seric ** Returns: 454535Seric ** none. 464535Seric ** 474535Seric ** Side Effects: 484535Seric ** Waits until some interesting activity occurs. When 494535Seric ** it does, a child is created to process it, and the 504535Seric ** parent waits for completion. Return from this 519886Seric ** routine is always in the child. The file pointers 529886Seric ** "InChannel" and "OutChannel" should be set to point 539886Seric ** to the communication channel. 544535Seric */ 554535Seric 567117Seric # define MAXCONNS 4 /* maximum simultaneous sendmails */ 577117Seric 589610Seric struct sockaddr_in SendmailAddress; 599610Seric 604535Seric getrequests() 614535Seric { 629610Seric int s; 639610Seric int t; 647117Seric union wait status; 657117Seric int numconnections = 0; 669610Seric register struct servent *sp; 677117Seric 689610Seric /* 699610Seric ** Set up the address for the mailer. 709610Seric */ 719610Seric 729610Seric sp = getservbyname("smtp", "tcp"); 739610Seric if (sp == NULL) 749610Seric { 759610Seric syserr("server \"smtp\" unknown"); 76*10167Seric goto severe; 779610Seric } 789610Seric SendmailAddress.sin_family = AF_INET; 799610Seric SendmailAddress.sin_addr.s_addr = INADDR_ANY; 809740Ssam SendmailAddress.sin_port = sp->s_port; 819610Seric 829610Seric /* 839610Seric ** Try to actually open the connection. 849610Seric */ 859610Seric 869610Seric # ifdef DEBUG 879610Seric if (tTd(15, 1)) 889610Seric printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 899610Seric # endif DEBUG 909610Seric 919610Seric /* get a socket for the SMTP connection */ 929610Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 939610Seric if (s < 0) 949610Seric { 959610Seric /* probably another daemon already */ 969610Seric syserr("getrequests: can't create socket"); 979610Seric severe: 989610Seric # ifdef LOG 999610Seric if (LogLevel > 0) 1009610Seric syslog(LOG_SALERT, "cannot get connection"); 1019610Seric # endif LOG 1029610Seric finis(); 1039610Seric } 1049610Seric if (bind(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 1059610Seric { 1069610Seric syserr("getrequests: cannot bind"); 1079610Seric close(s); 1089610Seric goto severe; 1099610Seric } 1109610Seric listen(s, 10); 1119610Seric 1129610Seric # ifdef DEBUG 1139610Seric if (tTd(15, 1)) 1149610Seric printf("getrequests: %d\n", s); 1159610Seric # endif DEBUG 1169610Seric 1174631Seric for (;;) 1184631Seric { 1199610Seric /* wait for a connection */ 1204636Seric register int pid; 1214631Seric 1229610Seric do 1239610Seric { 1249610Seric auto int lotherend; 1259610Seric struct sockaddr otherend; 1264631Seric 1279610Seric errno = 0; 1289610Seric lotherend = sizeof otherend; 1299610Seric t = accept(s, &otherend, &lotherend, 0); 1309610Seric } while (t < 0 && errno == EINTR); 1319610Seric if (t < 0) 1325978Seric { 1339610Seric syserr("getrequests: accept"); 1349610Seric sleep(5); 1359610Seric continue; 1365978Seric } 1374631Seric 1385978Seric /* 1395978Seric ** Create a subprocess to process the mail. 1405978Seric */ 1415978Seric 1425978Seric # ifdef DEBUG 1437677Seric if (tTd(15, 2)) 1449610Seric printf("getrequests: forking (fd = %d)\n", t); 1455978Seric # endif DEBUG 1465978Seric 1474636Seric pid = fork(); 1484636Seric if (pid < 0) 1494631Seric { 1504636Seric syserr("daemon: cannot fork"); 1514636Seric sleep(10); 1529610Seric (void) close(t); 1534636Seric continue; 1544631Seric } 1554631Seric 1564636Seric if (pid == 0) 1574631Seric { 1584636Seric /* 1594636Seric ** CHILD -- return to caller. 1604636Seric ** Verify calling user id if possible here. 1614636Seric */ 1624631Seric 1639610Seric close(s); 1649610Seric InChannel = fdopen(t, "r"); 1659610Seric OutChannel = fdopen(t, "w"); 1665978Seric # ifdef DEBUG 1677677Seric if (tTd(15, 2)) 1685978Seric printf("getreq: returning\n"); 1695978Seric # endif DEBUG 1707876Seric # ifdef LOG 1717876Seric if (LogLevel > 11) 1727876Seric syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 1737876Seric # endif LOG 1744636Seric return; 1754631Seric } 1764631Seric 1774636Seric /* 1784636Seric ** PARENT -- wait for child to terminate. 1794636Seric ** Perhaps we should allow concurrent processing? 1804636Seric */ 1814631Seric 1825978Seric # ifdef DEBUG 1837677Seric if (tTd(15, 2)) 1845978Seric { 1855978Seric sleep(2); 1865978Seric printf("getreq: parent waiting\n"); 1875978Seric } 1885978Seric # endif DEBUG 1895978Seric 1907117Seric /* close the port so that others will hang (for a while) */ 1919610Seric (void) close(t); 1927117Seric 1937117Seric /* pick up old zombies; implement load limiting */ 1947117Seric numconnections++; 1957117Seric while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 1967117Seric numconnections--; 1974631Seric } 1989886Seric /*NOTREACHED*/ 1994631Seric } 2005978Seric /* 2016039Seric ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 2026039Seric ** 2036039Seric ** Parameters: 2046039Seric ** host -- the name of the host. 2056633Seric ** port -- the port number to connect to. 2066039Seric ** outfile -- a pointer to a place to put the outfile 2076039Seric ** descriptor. 2086039Seric ** infile -- ditto for infile. 2096039Seric ** 2106039Seric ** Returns: 2116039Seric ** An exit code telling whether the connection could be 2126039Seric ** made and if not why not. 2136039Seric ** 2146039Seric ** Side Effects: 2156039Seric ** none. 2166039Seric */ 2175978Seric 2186633Seric makeconnection(host, port, outfile, infile) 2196039Seric char *host; 2207286Seric u_short port; 2216039Seric FILE **outfile; 2226039Seric FILE **infile; 2236039Seric { 2246039Seric register int s; 2256039Seric 2266039Seric /* 2276039Seric ** Set up the address for the mailer. 2289308Seric ** Accept "[a.b.c.d]" syntax for host name. 2296039Seric */ 2306039Seric 2319308Seric if (host[0] == '[') 2329308Seric { 2339308Seric long hid = 0; 2349308Seric int i, j; 2359308Seric register char *p = host; 2369308Seric 2379308Seric for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 2389308Seric { 2399308Seric j = 0; 2409308Seric while (isdigit(*++p)) 2419308Seric j = j * 10 + (*p - '0'); 2429308Seric if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 2439308Seric break; 2449308Seric hid |= j << ((3 - i) * 8); 2459308Seric } 2469308Seric if (i >= 0 || *p != ']' || *++p != '\0') 2479308Seric { 2489308Seric usrerr("Invalid numeric domain spec \"%s\"", host); 2499308Seric return (EX_NOHOST); 2509308Seric } 2519308Seric SendmailAddress.sin_addr.s_addr = hid; 2529308Seric } 2539610Seric else 2549610Seric { 2559610Seric register struct hostent *hp = gethostbyname(host); 2569610Seric 2579610Seric if (hp == 0) 2589610Seric return (EX_NOHOST); 2599610Seric bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 2609610Seric } 2619610Seric 2629610Seric /* 2639610Seric ** Determine the port number. 2649610Seric */ 2659610Seric 26610011Seric if (port != 0) 26710011Seric SendmailAddress.sin_port = htons(port); 26810011Seric else 2699610Seric { 2709610Seric register struct servent *sp = getservbyname("smtp", "tcp"); 2719610Seric 2729610Seric if (sp == NULL) 2739610Seric { 2749610Seric syserr("makeconnection: server \"smtp\" unknown"); 2759610Seric return (EX_OSFILE); 2769610Seric } 27710011Seric SendmailAddress.sin_port = sp->s_port; 2789610Seric } 2796039Seric 2806039Seric /* 2816039Seric ** Try to actually open the connection. 2826039Seric */ 2836039Seric 2846039Seric # ifdef DEBUG 2857677Seric if (tTd(16, 1)) 2866039Seric printf("makeconnection (%s)\n", host); 2876039Seric # endif DEBUG 2886039Seric 2899310Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 2906039Seric if (s < 0) 2916039Seric { 2926039Seric syserr("makeconnection: no socket"); 2936039Seric goto failure; 2946039Seric } 2956039Seric 2966039Seric # ifdef DEBUG 2977677Seric if (tTd(16, 1)) 2986039Seric printf("makeconnection: %d\n", s); 2996039Seric # endif DEBUG 3009546Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3019610Seric SendmailAddress.sin_family = AF_INET; 3029310Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 3039310Seric if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 3046039Seric { 3056039Seric /* failure, decide if temporary or not */ 3066039Seric failure: 3076039Seric switch (errno) 3086039Seric { 3096039Seric case EISCONN: 3106039Seric case ETIMEDOUT: 3116897Seric case EINPROGRESS: 3126897Seric case EALREADY: 3136897Seric case EADDRINUSE: 31410123Seric case EHOSTDOWN: 3156897Seric case ENETDOWN: 3166897Seric case ENETRESET: 3176897Seric case ENOBUFS: 3187204Seric case ECONNREFUSED: 31910081Seric case EHOSTUNREACH: 32010098Seric case ENETUNREACH: 3216039Seric /* there are others, I'm sure..... */ 3226039Seric return (EX_TEMPFAIL); 3236039Seric 3246039Seric default: 3256039Seric return (EX_UNAVAILABLE); 3266039Seric } 3276039Seric } 3286039Seric 3296039Seric /* connection ok, put it into canonical form */ 3306039Seric *outfile = fdopen(s, "w"); 3316039Seric *infile = fdopen(s, "r"); 3326039Seric 33310098Seric return (EX_OK); 3346039Seric } 3356039Seric 3365978Seric #endif DAEMON 337