1 # include <errno.h> 2 # include "sendmail.h" 3 4 #ifndef DAEMON 5 SCCSID(@(#)daemon.c 3.20 07/27/82 (w/o daemon mode)); 6 #else 7 8 # include <sys/socket.h> 9 # include <net/in.h> 10 # include <wait.h> 11 12 SCCSID(@(#)daemon.c 3.20 07/27/82 (with daemon mode)); 13 14 /* 15 ** DAEMON.C -- routines to use when running as a daemon. 16 ** 17 ** This entire file is highly dependent on the 4.2 BSD 18 ** interprocess communication primitives. No attempt has 19 ** been made to make this file portable to Version 7, 20 ** Version 6, MPX files, etc. If you should try such a 21 ** thing yourself, I recommend chucking the entire file 22 ** and starting from scratch. Basic semantics are: 23 ** 24 ** getrequests() 25 ** Opens a port and initiates a connection. 26 ** Returns in a child. Must set InChannel and 27 ** OutChannel appropriately. 28 ** makeconnection(host, port, outfile, infile) 29 ** Make a connection to the named host on the given 30 ** port. Set *outfile and *infile to the files 31 ** appropriate for communication. Returns zero on 32 ** success, else an exit status describing the 33 ** error. 34 ** 35 ** The semantics of both of these should be clean. 36 */ 37 /* 38 ** GETREQUESTS -- open mail IPC port and get requests. 39 ** 40 ** Parameters: 41 ** none. 42 ** 43 ** Returns: 44 ** none. 45 ** 46 ** Side Effects: 47 ** Waits until some interesting activity occurs. When 48 ** it does, a child is created to process it, and the 49 ** parent waits for completion. Return from this 50 ** routine is always in the child. 51 */ 52 53 # define MAXCONNS 4 /* maximum simultaneous sendmails */ 54 55 getrequests() 56 { 57 union wait status; 58 int numconnections = 0; 59 60 for (;;) 61 { 62 register int pid; 63 register int port; 64 65 /* 66 ** Wait for a connection. 67 */ 68 69 while ((port = getconnection()) < 0) 70 { 71 syserr("getrequests: getconnection failed"); 72 finis(); 73 } 74 75 /* 76 ** Create a subprocess to process the mail. 77 */ 78 79 # ifdef DEBUG 80 if (Debug > 1) 81 printf("getrequests: forking (port = %d)\n", port); 82 # endif DEBUG 83 84 pid = fork(); 85 if (pid < 0) 86 { 87 syserr("daemon: cannot fork"); 88 sleep(10); 89 (void) close(port); 90 continue; 91 } 92 93 if (pid == 0) 94 { 95 /* 96 ** CHILD -- return to caller. 97 ** Verify calling user id if possible here. 98 */ 99 100 InChannel = fdopen(port, "r"); 101 OutChannel = fdopen(port, "w"); 102 # ifdef DEBUG 103 if (Debug > 1) 104 printf("getreq: returning\n"); 105 # endif DEBUG 106 return; 107 } 108 109 /* 110 ** PARENT -- wait for child to terminate. 111 ** Perhaps we should allow concurrent processing? 112 */ 113 114 # ifdef DEBUG 115 if (Debug > 1) 116 { 117 sleep(2); 118 printf("getreq: parent waiting\n"); 119 } 120 # endif DEBUG 121 122 /* close the port so that others will hang (for a while) */ 123 (void) close(port); 124 125 /* pick up old zombies; implement load limiting */ 126 numconnections++; 127 while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 128 numconnections--; 129 } 130 } 131 /* 132 ** GETCONNECTION -- make a connection with the outside world 133 ** 134 ** Parameters: 135 ** none. 136 ** 137 ** Returns: 138 ** The port for mail traffic. 139 ** 140 ** Side Effects: 141 ** Waits for a connection. 142 */ 143 144 #define IPPORT_PLAYPORT 3055 /* random number */ 145 146 struct sockaddr_in SendmailAddress = { AF_INET, IPPORT_SMTP }; 147 148 getconnection() 149 { 150 register int s; 151 struct sockaddr otherend; 152 153 /* 154 ** Set up the address for the mailer. 155 */ 156 157 SendmailAddress.sin_addr.s_addr = 0; 158 SendmailAddress.sin_port = IPPORT_SMTP; 159 # ifdef DEBUG 160 if (Debug > 0) 161 SendmailAddress.sin_port = IPPORT_PLAYPORT; 162 # endif DEBUG 163 SendmailAddress.sin_port = htons(SendmailAddress.sin_port); 164 165 /* 166 ** Try to actually open the connection. 167 */ 168 169 # ifdef DEBUG 170 if (Debug) 171 printf("getconnection\n"); 172 # endif DEBUG 173 174 for (;;) 175 { 176 /* get a socket for the SMTP connection */ 177 s = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN); 178 if (s < 0) 179 { 180 /* probably another daemon already */ 181 syserr("getconnection: can't create socket"); 182 break; 183 } 184 185 # ifdef DEBUG 186 if (Debug) 187 printf("getconnection: %d\n", s); 188 # endif DEBUG 189 190 /* wait for a connection */ 191 if (accept(s, &otherend) >= 0) 192 break; 193 194 /* probably innocuous -- retry */ 195 syserr("getconnection: accept"); 196 (void) close(s); 197 sleep(20); 198 } 199 200 return (s); 201 } 202 /* 203 ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 204 ** 205 ** Parameters: 206 ** host -- the name of the host. 207 ** port -- the port number to connect to. 208 ** outfile -- a pointer to a place to put the outfile 209 ** descriptor. 210 ** infile -- ditto for infile. 211 ** 212 ** Returns: 213 ** An exit code telling whether the connection could be 214 ** made and if not why not. 215 ** 216 ** Side Effects: 217 ** none. 218 */ 219 220 makeconnection(host, port, outfile, infile) 221 char *host; 222 u_short port; 223 FILE **outfile; 224 FILE **infile; 225 { 226 register int s; 227 228 /* 229 ** Set up the address for the mailer. 230 */ 231 232 if ((SendmailAddress.sin_addr.s_addr = rhost(&host)) == -1) 233 return (EX_NOHOST); 234 if (port == 0) 235 port = IPPORT_SMTP; 236 SendmailAddress.sin_port = htons(port); 237 238 /* 239 ** Try to actually open the connection. 240 */ 241 242 # ifdef DEBUG 243 if (Debug) 244 printf("makeconnection (%s)\n", host); 245 # endif DEBUG 246 247 s = socket(SOCK_STREAM, 0, (struct sockaddr_in *) 0, 0); 248 if (s < 0) 249 { 250 syserr("makeconnection: no socket"); 251 goto failure; 252 } 253 254 # ifdef DEBUG 255 if (Debug) 256 printf("makeconnection: %d\n", s); 257 # endif DEBUG 258 fflush(Xscript); /* for debugging */ 259 if (connect(s, &SendmailAddress) < 0) 260 { 261 /* failure, decide if temporary or not */ 262 failure: 263 switch (errno) 264 { 265 case EISCONN: 266 case ETIMEDOUT: 267 case EINPROGRESS: 268 case EALREADY: 269 case EADDRINUSE: 270 case ENETDOWN: 271 case ENETRESET: 272 case ENOBUFS: 273 case ECONNREFUSED: 274 /* there are others, I'm sure..... */ 275 return (EX_TEMPFAIL); 276 277 default: 278 return (EX_UNAVAILABLE); 279 } 280 } 281 282 /* connection ok, put it into canonical form */ 283 *outfile = fdopen(s, "w"); 284 *infile = fdopen(s, "r"); 285 286 return (0); 287 } 288 289 #endif DAEMON 290