16039Seric # include <errno.h> 24535Seric # include "sendmail.h" 34535Seric 45978Seric #ifndef DAEMON 5*10342Seric SCCSID(@(#)daemon.c 3.45 01/16/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*10342Seric SCCSID(@(#)daemon.c 3.45 01/16/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. 2910206Seric ** clrdaemon() 3010206Seric ** Close any open files associated with getting 3110206Seric ** the connection; this is used when running the queue, 3210206Seric ** etc., to avoid having extra file descriptors during 3310206Seric ** the queue run and to avoid confusing the network 3410206Seric ** 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 6210206Seric struct sockaddr_in SendmailAddress;/* internet address of sendmail */ 6310206Seric int DaemonSocket = -1; /* fd describing socket */ 64*10342Seric int MaxConnections = 4; /* maximum simultaneous sendmails */ 659610Seric 664535Seric getrequests() 674535Seric { 689610Seric int t; 697117Seric union wait status; 707117Seric int numconnections = 0; 719610Seric register struct servent *sp; 727117Seric 739610Seric /* 749610Seric ** Set up the address for the mailer. 759610Seric */ 769610Seric 779610Seric sp = getservbyname("smtp", "tcp"); 789610Seric if (sp == NULL) 799610Seric { 809610Seric syserr("server \"smtp\" unknown"); 8110167Seric goto severe; 829610Seric } 839610Seric SendmailAddress.sin_family = AF_INET; 849610Seric SendmailAddress.sin_addr.s_addr = INADDR_ANY; 859740Ssam SendmailAddress.sin_port = sp->s_port; 869610Seric 879610Seric /* 889610Seric ** Try to actually open the connection. 899610Seric */ 909610Seric 919610Seric # ifdef DEBUG 929610Seric if (tTd(15, 1)) 939610Seric printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 949610Seric # endif DEBUG 959610Seric 969610Seric /* get a socket for the SMTP connection */ 9710206Seric DaemonSocket = socket(AF_INET, SOCK_STREAM, 0, 0); 9810206Seric if (DaemonSocket < 0) 999610Seric { 1009610Seric /* probably another daemon already */ 1019610Seric syserr("getrequests: can't create socket"); 1029610Seric severe: 1039610Seric # ifdef LOG 1049610Seric if (LogLevel > 0) 1059610Seric syslog(LOG_SALERT, "cannot get connection"); 1069610Seric # endif LOG 1079610Seric finis(); 1089610Seric } 10910206Seric if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 1109610Seric { 1119610Seric syserr("getrequests: cannot bind"); 11210206Seric (void) close(DaemonSocket); 1139610Seric goto severe; 1149610Seric } 11510206Seric listen(DaemonSocket, 10); 1169610Seric 1179610Seric # ifdef DEBUG 1189610Seric if (tTd(15, 1)) 11910206Seric printf("getrequests: %d\n", DaemonSocket); 1209610Seric # endif DEBUG 1219610Seric 1224631Seric for (;;) 1234631Seric { 1249610Seric /* wait for a connection */ 1254636Seric register int pid; 1264631Seric 1279610Seric do 1289610Seric { 1299610Seric auto int lotherend; 1309610Seric struct sockaddr otherend; 1314631Seric 1329610Seric errno = 0; 1339610Seric lotherend = sizeof otherend; 13410206Seric t = accept(DaemonSocket, &otherend, &lotherend, 0); 1359610Seric } while (t < 0 && errno == EINTR); 1369610Seric if (t < 0) 1375978Seric { 1389610Seric syserr("getrequests: accept"); 1399610Seric sleep(5); 1409610Seric continue; 1415978Seric } 1424631Seric 1435978Seric /* 1445978Seric ** Create a subprocess to process the mail. 1455978Seric */ 1465978Seric 1475978Seric # ifdef DEBUG 1487677Seric if (tTd(15, 2)) 1499610Seric printf("getrequests: forking (fd = %d)\n", t); 1505978Seric # endif DEBUG 1515978Seric 1524636Seric pid = fork(); 1534636Seric if (pid < 0) 1544631Seric { 1554636Seric syserr("daemon: cannot fork"); 1564636Seric sleep(10); 1579610Seric (void) close(t); 1584636Seric continue; 1594631Seric } 1604631Seric 1614636Seric if (pid == 0) 1624631Seric { 1634636Seric /* 1644636Seric ** CHILD -- return to caller. 1654636Seric ** Verify calling user id if possible here. 1664636Seric */ 1674631Seric 16810206Seric (void) close(DaemonSocket); 1699610Seric InChannel = fdopen(t, "r"); 1709610Seric OutChannel = fdopen(t, "w"); 1715978Seric # ifdef DEBUG 1727677Seric if (tTd(15, 2)) 1735978Seric printf("getreq: returning\n"); 1745978Seric # endif DEBUG 1757876Seric # ifdef LOG 1767876Seric if (LogLevel > 11) 1777876Seric syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 1787876Seric # endif LOG 1794636Seric return; 1804631Seric } 1814631Seric 1824636Seric /* 1834636Seric ** PARENT -- wait for child to terminate. 1844636Seric ** Perhaps we should allow concurrent processing? 1854636Seric */ 1864631Seric 1875978Seric # ifdef DEBUG 1887677Seric if (tTd(15, 2)) 1895978Seric { 1905978Seric sleep(2); 1915978Seric printf("getreq: parent waiting\n"); 1925978Seric } 1935978Seric # endif DEBUG 1945978Seric 1957117Seric /* close the port so that others will hang (for a while) */ 1969610Seric (void) close(t); 1977117Seric 1987117Seric /* pick up old zombies; implement load limiting */ 1997117Seric numconnections++; 200*10342Seric while (wait3(&status, numconnections < MaxConnections ? WNOHANG : 0, 0) > 0) 2017117Seric numconnections--; 2024631Seric } 2039886Seric /*NOTREACHED*/ 2044631Seric } 2055978Seric /* 20610206Seric ** CLRDAEMON -- reset the daemon connection 20710206Seric ** 20810206Seric ** Parameters: 20910206Seric ** none. 21010206Seric ** 21110206Seric ** Returns: 21210206Seric ** none. 21310206Seric ** 21410206Seric ** Side Effects: 21510206Seric ** releases any resources used by the passive daemon. 21610206Seric */ 21710206Seric 21810206Seric clrdaemon() 21910206Seric { 22010206Seric if (DaemonSocket >= 0) 22110206Seric (void) close(DaemonSocket); 22210206Seric DaemonSocket = -1; 22310206Seric } 22410206Seric /* 2256039Seric ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 2266039Seric ** 2276039Seric ** Parameters: 2286039Seric ** host -- the name of the host. 2296633Seric ** port -- the port number to connect to. 2306039Seric ** outfile -- a pointer to a place to put the outfile 2316039Seric ** descriptor. 2326039Seric ** infile -- ditto for infile. 2336039Seric ** 2346039Seric ** Returns: 2356039Seric ** An exit code telling whether the connection could be 2366039Seric ** made and if not why not. 2376039Seric ** 2386039Seric ** Side Effects: 2396039Seric ** none. 2406039Seric */ 2415978Seric 2426633Seric makeconnection(host, port, outfile, infile) 2436039Seric char *host; 2447286Seric u_short port; 2456039Seric FILE **outfile; 2466039Seric FILE **infile; 2476039Seric { 2486039Seric register int s; 2496039Seric 2506039Seric /* 2516039Seric ** Set up the address for the mailer. 2529308Seric ** Accept "[a.b.c.d]" syntax for host name. 2536039Seric */ 2546039Seric 2559308Seric if (host[0] == '[') 2569308Seric { 2579308Seric long hid = 0; 2589308Seric int i, j; 2599308Seric register char *p = host; 2609308Seric 2619308Seric for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 2629308Seric { 2639308Seric j = 0; 2649308Seric while (isdigit(*++p)) 2659308Seric j = j * 10 + (*p - '0'); 2669308Seric if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 2679308Seric break; 2689308Seric hid |= j << ((3 - i) * 8); 2699308Seric } 2709308Seric if (i >= 0 || *p != ']' || *++p != '\0') 2719308Seric { 2729308Seric usrerr("Invalid numeric domain spec \"%s\"", host); 2739308Seric return (EX_NOHOST); 2749308Seric } 2759308Seric SendmailAddress.sin_addr.s_addr = hid; 2769308Seric } 2779610Seric else 2789610Seric { 2799610Seric register struct hostent *hp = gethostbyname(host); 2809610Seric 2819610Seric if (hp == 0) 2829610Seric return (EX_NOHOST); 2839610Seric bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 2849610Seric } 2859610Seric 2869610Seric /* 2879610Seric ** Determine the port number. 2889610Seric */ 2899610Seric 29010011Seric if (port != 0) 29110011Seric SendmailAddress.sin_port = htons(port); 29210011Seric else 2939610Seric { 2949610Seric register struct servent *sp = getservbyname("smtp", "tcp"); 2959610Seric 2969610Seric if (sp == NULL) 2979610Seric { 2989610Seric syserr("makeconnection: server \"smtp\" unknown"); 2999610Seric return (EX_OSFILE); 3009610Seric } 30110011Seric SendmailAddress.sin_port = sp->s_port; 3029610Seric } 3036039Seric 3046039Seric /* 3056039Seric ** Try to actually open the connection. 3066039Seric */ 3076039Seric 3086039Seric # ifdef DEBUG 3097677Seric if (tTd(16, 1)) 3106039Seric printf("makeconnection (%s)\n", host); 3116039Seric # endif DEBUG 3126039Seric 3139310Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 3146039Seric if (s < 0) 3156039Seric { 3166039Seric syserr("makeconnection: no socket"); 3176039Seric goto failure; 3186039Seric } 3196039Seric 3206039Seric # ifdef DEBUG 3217677Seric if (tTd(16, 1)) 3226039Seric printf("makeconnection: %d\n", s); 3236039Seric # endif DEBUG 3249546Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3259610Seric SendmailAddress.sin_family = AF_INET; 3269310Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 3279310Seric if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 3286039Seric { 3296039Seric /* failure, decide if temporary or not */ 3306039Seric failure: 3316039Seric switch (errno) 3326039Seric { 3336039Seric case EISCONN: 3346039Seric case ETIMEDOUT: 3356897Seric case EINPROGRESS: 3366897Seric case EALREADY: 3376897Seric case EADDRINUSE: 33810123Seric case EHOSTDOWN: 3396897Seric case ENETDOWN: 3406897Seric case ENETRESET: 3416897Seric case ENOBUFS: 3427204Seric case ECONNREFUSED: 34310081Seric case EHOSTUNREACH: 34410098Seric case ENETUNREACH: 3456039Seric /* there are others, I'm sure..... */ 3466039Seric return (EX_TEMPFAIL); 3476039Seric 3486039Seric default: 3496039Seric return (EX_UNAVAILABLE); 3506039Seric } 3516039Seric } 3526039Seric 3536039Seric /* connection ok, put it into canonical form */ 3546039Seric *outfile = fdopen(s, "w"); 3556039Seric *infile = fdopen(s, "r"); 3566039Seric 35710098Seric return (EX_OK); 3586039Seric } 3596039Seric 3605978Seric #endif DAEMON 361