spawn.c (6417B)
1 #include "spawn.h" 2 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include "sig.h" 6 #include "wait.h" 7 #include "substdio.h" 8 #include "byte.h" 9 #include "str.h" 10 #include "alloc.h" 11 #include "stralloc.h" 12 #include "select.h" 13 #include "exit.h" 14 #include "coe.h" 15 #include "open.h" 16 #include "error.h" 17 #include "auto_qmail.h" 18 #include "auto_uids.h" 19 #include "auto_spawn.h" 20 21 uid_t auto_uidq; 22 23 struct delivery 24 { 25 int used; 26 int fdin; /* pipe input */ 27 int pid; /* zero if child is dead */ 28 int wstat; /* if !pid: status of child */ 29 int fdout; /* pipe output, -1 if !pid; delays eof until after death */ 30 stralloc output; 31 } 32 ; 33 34 struct delivery *d; 35 36 void sigchld() 37 { 38 int wstat; 39 int pid; 40 int i; 41 while ((pid = wait_nohang(&wstat)) > 0) 42 for (i = 0;i < auto_spawn;++i) if (d[i].used) 43 if (d[i].pid == pid) 44 { 45 close(d[i].fdout); d[i].fdout = -1; 46 d[i].wstat = wstat; d[i].pid = 0; 47 } 48 } 49 50 int flagwriting = 1; 51 52 ssize_t okwrite(int fd, const void *buf, size_t n) 53 { 54 int w; 55 if (!flagwriting) return n; 56 w = write(fd,buf,n); 57 if (w != -1) return w; 58 if (errno == error_intr) return -1; 59 flagwriting = 0; close(fd); 60 return n; 61 } 62 63 int flagreading = 1; 64 char outbuf[1024]; substdio ssout; 65 66 int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ 67 int flagabort = 0; /* if 1, everything except delnum is garbage */ 68 int delnum; 69 stralloc messid = {0}; 70 stralloc sender = {0}; 71 stralloc recip = {0}; 72 73 void err(s) char *s; 74 { 75 char ch; ch = delnum; substdio_put(&ssout,&ch,1); 76 substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1); 77 } 78 79 void docmd() 80 { 81 int f; 82 int i; 83 int j; 84 int fdmess; 85 int pi[2]; 86 struct stat st; 87 88 if (flagabort) { err("Z4.3.0 qmail-spawn out of memory.\n"); return; } 89 if (delnum < 0) { err("Z4.3.5 Internal error: delnum negative.\n"); return; } 90 if (delnum >= auto_spawn) { err("Z4.3.5 Internal error: delnum too big.\n"); return; } 91 if (d[delnum].used) { err("Z4.3.5 Internal error: delnum in use.\n"); return; } 92 for (i = 0;i < messid.len;++i) 93 if (messid.s[i]) 94 if (!i || (messid.s[i] != '/')) 95 if ((unsigned char) (messid.s[i] - '0') > 9) 96 { err("D5.3.5 Internal error: messid has nonnumerics.\n"); return; } 97 if (messid.len > 100) { err("D5.3.5 Internal error: messid too long.\n"); return; } 98 if (!messid.s[0]) { err("D5.3.5 Internal error: messid too short.\n"); return; } 99 100 if (!stralloc_copys(&d[delnum].output,"")) 101 { err("Z4.3.0 qmail-spawn out of memory.\n"); return; } 102 103 j = byte_rchr(recip.s,recip.len,'@'); 104 if (j >= recip.len) { err("D5.1.3 Sorry, address must include host name.\n"); return; } 105 106 fdmess = open_read(messid.s); 107 if (fdmess == -1) { err("Z4.3.0 qmail-spawn unable to open message.\n"); return; } 108 109 if (fstat(fdmess,&st) == -1) 110 { close(fdmess); err("Z4.3.0 qmail-spawn unable to fstat message.\n"); return; } 111 if ((st.st_mode & S_IFMT) != S_IFREG) 112 { close(fdmess); err("Z4.3.5 Sorry, message has wrong type.\n"); return; } 113 if (st.st_uid != auto_uidq) /* aaack! qmailq has to be trusted! */ 114 /* your security is already toast at this point. damage control... */ 115 { close(fdmess); err("Z4.3.5 Sorry, message has wrong owner.\n"); return; } 116 117 if (pipe(pi) == -1) 118 { close(fdmess); err("Z4.3.0 qmail-spawn unable to create pipe.\n"); return; } 119 120 coe(pi[0]); 121 122 f = spawn(fdmess,pi[1],sender.s,recip.s,j); 123 close(fdmess); 124 if (f == -1) 125 { close(pi[0]); close(pi[1]); err("Z4.3.0 qmail-spawn unable to fork.\n"); return; } 126 127 d[delnum].fdin = pi[0]; 128 d[delnum].fdout = pi[1]; coe(pi[1]); 129 d[delnum].pid = f; 130 d[delnum].used = 1; 131 } 132 133 char cmdbuf[1024]; 134 135 void getcmd() 136 { 137 int i; 138 int r; 139 char ch; 140 141 r = read(0,cmdbuf,sizeof(cmdbuf)); 142 if (r == 0) 143 { flagreading = 0; return; } 144 if (r == -1) 145 { 146 if (errno != error_intr) 147 flagreading = 0; 148 return; 149 } 150 151 for (i = 0;i < r;++i) 152 { 153 ch = cmdbuf[i]; 154 switch(stage) 155 { 156 case 0: 157 delnum = (unsigned int) (unsigned char) ch; 158 messid.len = 0; stage = 1; break; 159 case 1: 160 if (!stralloc_append(&messid,&ch)) flagabort = 1; 161 if (ch) break; 162 sender.len = 0; stage = 2; break; 163 case 2: 164 if (!stralloc_append(&sender,&ch)) flagabort = 1; 165 if (ch) break; 166 recip.len = 0; stage = 3; break; 167 case 3: 168 if (!stralloc_append(&recip,&ch)) flagabort = 1; 169 if (ch) break; 170 docmd(); 171 flagabort = 0; stage = 0; break; 172 } 173 } 174 } 175 176 char inbuf[128]; 177 178 int main(int argc, char **argv) 179 { 180 char ch; 181 int i; 182 int r; 183 fd_set rfds; 184 int nfds; 185 186 if (chdir(auto_qmail) == -1) _exit(111); 187 if (chdir("queue/mess") == -1) _exit(111); 188 if (!stralloc_copys(&messid,"")) _exit(111); 189 if (!stralloc_copys(&sender,"")) _exit(111); 190 if (!stralloc_copys(&recip,"")) _exit(111); 191 192 d = (struct delivery *) alloc((auto_spawn + 10) * sizeof(struct delivery)); 193 if (!d) _exit(111); 194 195 substdio_fdbuf(&ssout,okwrite,1,outbuf,sizeof(outbuf)); 196 197 sig_pipeignore(); 198 sig_childcatch(sigchld); 199 200 initialize(argc,argv); 201 202 ch = auto_spawn; substdio_putflush(&ssout,&ch,1); 203 204 for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; } 205 206 for (;;) 207 { 208 if (!flagreading) 209 { 210 for (i = 0;i < auto_spawn;++i) if (d[i].used) break; 211 if (i >= auto_spawn) _exit(0); 212 } 213 sig_childunblock(); 214 215 FD_ZERO(&rfds); 216 if (flagreading) FD_SET(0,&rfds); 217 nfds = 1; 218 for (i = 0;i < auto_spawn;++i) if (d[i].used) 219 { FD_SET(d[i].fdin,&rfds); if (d[i].fdin >= nfds) nfds = d[i].fdin + 1; } 220 221 r = select(nfds,&rfds,NULL,NULL,NULL); 222 sig_childblock(); 223 224 if (r != -1) 225 { 226 if (flagreading) 227 if (FD_ISSET(0,&rfds)) 228 getcmd(); 229 for (i = 0;i < auto_spawn;++i) if (d[i].used) 230 if (FD_ISSET(d[i].fdin,&rfds)) 231 { 232 r = read(d[i].fdin,inbuf,128); 233 if (r == -1) 234 continue; /* read error on a readable pipe? be serious */ 235 if (r == 0) 236 { 237 ch = i; substdio_put(&ssout,&ch,1); 238 report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len); 239 substdio_put(&ssout,"",1); 240 substdio_flush(&ssout); 241 close(d[i].fdin); d[i].used = 0; 242 continue; 243 } 244 while (!stralloc_readyplus(&d[i].output,r)) sleep(10); /*XXX sleep until we can get memory*/ 245 byte_copy(d[i].output.s + d[i].output.len,r,inbuf); 246 d[i].output.len += r; 247 if (truncreport > 100) 248 if (d[i].output.len > truncreport) 249 { 250 char *truncmess = "\nError report too long, sorry.\n"; 251 d[i].output.len = truncreport - str_len(truncmess) - 3; 252 stralloc_cats(&d[i].output,truncmess); 253 } 254 } 255 } 256 } 257 }