1 /* lpd.c 4.1 83/04/29 */ 2 /* 3 * lpd -- line printer daemon. 4 * 5 * Listen for a connection and perform the requested operation. 6 * Operations are: 7 * \1printer\n 8 * check the queue for jobs and print any found. 9 * \2printer\n 10 * receive a job from another machine and queue it. 11 * \3printer [users ...] [jobs ...]\n 12 * return the current state of the queue (short form). 13 * \4printer [users ...] [jobs ...]\n 14 * return the current state of the queue (long form). 15 * \5printer person [users ...] [jobs ...]\n 16 * remove jobs from the queue. 17 * 18 * Strategy to maintain protected spooling area: 19 * 1. Spooling area is writable only by daemon and spooling group 20 * 2. lpr runs setuid root and setgrp spooling group; it uses 21 * root to access any file it wants (verifying things before 22 * with an access call) and group id to know how it should 23 * set up ownership of files in the spooling area. 24 * 3. Files in spooling area are owned by the owner, group spooling 25 * group, with mode 660. 26 * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to 27 * access files and printer. Users can't get to anything 28 * w/o help of lpq and lprm programs. 29 */ 30 31 #include "lp.h" 32 33 char *logfile = DEFLOGF; 34 struct sockaddr_in sin = { AF_INET }; 35 int reapchild(); 36 char *ntoa(); 37 38 main(argc, argv) 39 int argc; 40 char **argv; 41 { 42 int f, options; 43 struct sockaddr_in from; 44 struct servent *sp; 45 46 gethostname(host, sizeof(host)); 47 name = argv[0]; 48 49 sp = getservbyname("printer", "tcp"); 50 if (sp == NULL) { 51 log("printer/tcp: unknown service"); 52 exit(1); 53 } 54 sin.sin_port = sp->s_port; 55 56 while (--argc > 0) { 57 argv++; 58 if (argv[0][0] == '-') 59 switch (argv[0][1]) { 60 case 'd': 61 options |= SO_DEBUG; 62 break; 63 case 'l': 64 argc--; 65 logfile = *++argv; 66 break; 67 } 68 else { 69 int port = atoi(argv[0]); 70 71 if (port < 0) { 72 fprintf(stderr, "%s: bad port #\n", argv[0]); 73 exit(1); 74 } 75 sin.sin_port = htons((u_short) port); 76 } 77 } 78 #ifndef DEBUG 79 /* 80 * Set up standard environment by detaching from the parent. 81 */ 82 if (fork()) 83 exit(0); 84 for (f = 0; f < 3; f++) 85 (void) close(f); 86 (void) open("/dev/null", FRDONLY, 0); 87 (void) open("/dev/null", FWRONLY, 0); 88 (void) open(logfile, FWRONLY|FAPPEND, 0); 89 f = open("/dev/tty", FRDWR, 0); 90 if (f > 0) { 91 ioctl(f, TIOCNOTTY, 0); 92 (void) close(f); 93 } 94 #endif 95 (void) umask(0); 96 f = socket(AF_INET, SOCK_STREAM, 0); 97 if (f < 0) { 98 logerror("socket"); 99 exit(1); 100 } 101 if (options & SO_DEBUG) 102 if (setsockopt(f, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 103 logerror("setsockopt (SO_DEBUG)"); 104 if (bind(f, &sin, sizeof(sin), 0) < 0) { 105 logerror("bind"); 106 exit(1); 107 } 108 /* 109 * Restart all the printers and tell everyone that we are 110 * up and running. 111 */ 112 startup(); 113 /* 114 * Main loop: listen, accept, do a request, continue. 115 */ 116 sigset(SIGCHLD, reapchild); 117 listen(f, 10); 118 for (;;) { 119 int s, len = sizeof(from); 120 121 s = accept(f, &from, &len, 0); 122 if (s < 0) { 123 if (errno == EINTR) 124 continue; 125 logerror("accept"); 126 continue; 127 } 128 if (fork() == 0) { 129 sigset(SIGCHLD, SIG_IGN); 130 (void) close(f); 131 doit(s, &from); 132 exit(0); 133 } 134 (void) close(s); 135 } 136 } 137 138 reapchild() 139 { 140 union wait status; 141 142 while (wait3(&status, WNOHANG, 0) > 0) 143 ; 144 } 145 146 /* 147 * Stuff for handling job specifications 148 */ 149 char *user[MAXUSERS]; /* users to process */ 150 int users; /* # of users in user array */ 151 int requ[MAXREQUESTS]; /* job number of spool entries */ 152 int requests; /* # of spool requests */ 153 154 char cbuf[BUFSIZ]; /* command line buffer */ 155 char fromb[32]; /* buffer for client's machine name */ 156 157 doit(f, fromp) 158 int f; 159 struct sockaddr_in *fromp; 160 { 161 register char *cp; 162 register struct hostent *hp; 163 register int n; 164 extern char *person; 165 char c; 166 167 dup2(f, 1); 168 (void) close(f); 169 f = 1; 170 fromp->sin_port = ntohs(fromp->sin_port); 171 if (fromp->sin_family != AF_INET || fromp->sin_port >= IPPORT_RESERVED) 172 fatal("Malformed from address"); 173 hp = gethostbyaddr(&fromp->sin_addr, sizeof(struct in_addr), 174 fromp->sin_family); 175 if (hp == 0) 176 fatal("Host name for your address (%s) unknown", 177 ntoa(fromp->sin_addr)); 178 strcpy(fromb, hp->h_name); 179 from = fromb; 180 for (;;) { 181 cp = cbuf; 182 do { 183 if ((n = read(f, &c, 1)) != 1) { 184 if (n < 0) 185 fatal("Lost connection"); 186 return; 187 } 188 if (cp >= &cbuf[sizeof(cbuf)]) 189 fatal("Command line too long"); 190 *cp++ = c; 191 } while (c != '\n'); 192 *--cp = '\0'; 193 cp = cbuf; 194 switch (*cp++) { 195 case '\1': /* check the queue and print any jobs there */ 196 printer = cp; 197 printjob(); 198 break; 199 case '\2': /* receive files to be queued */ 200 printer = cp; 201 recvjob(); 202 break; 203 case '\3': /* send back the short form queue status */ 204 case '\4': /* send back the long form queue status */ 205 printer = cp; 206 while (*cp) { 207 if (*cp != ' ') { 208 cp++; 209 continue; 210 } 211 *cp++ = '\0'; 212 while (isspace(*cp)) 213 cp++; 214 if (*cp == '\0') 215 break; 216 if (isdigit(*cp)) { 217 if (requests >= MAXREQUESTS) 218 fatal("Too many requests"); 219 requ[requests++] = atoi(cp); 220 } else { 221 if (users >= MAXUSERS) 222 fatal("Too many users"); 223 user[users++] = cp; 224 } 225 } 226 displayq(cbuf[0] - '\3'); 227 exit(0); 228 case '\5': /* remove a job from the queue */ 229 printer = cp; 230 while (*cp && *cp != ' ') 231 cp++; 232 if (!*cp) 233 break; 234 *cp++ = '\0'; 235 person = cp; 236 while (*cp) { 237 if (*cp != ' ') { 238 cp++; 239 continue; 240 } 241 *cp++ = '\0'; 242 while (isspace(*cp)) 243 cp++; 244 if (*cp == '\0') 245 break; 246 if (isdigit(*cp)) { 247 if (requests >= MAXREQUESTS) 248 fatal("Too many requests"); 249 requ[requests++] = atoi(cp); 250 } else { 251 if (users >= MAXUSERS) 252 fatal("Too many users"); 253 user[users++] = cp; 254 } 255 } 256 rmjob(); 257 break; 258 } 259 fatal("Illegal service request"); 260 exit(1); 261 } 262 } 263 264 /* 265 * Convert network-format internet address 266 * to base 256 d.d.d.d representation. 267 */ 268 char * 269 ntoa(in) 270 struct in_addr in; 271 { 272 static char b[18]; 273 register char *p; 274 275 p = (char *)∈ 276 #define UC(b) (((int)b)&0xff) 277 sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 278 return (b); 279 } 280 281 /*VARARGS1*/ 282 log(msg, a1, a2, a3) 283 char *msg; 284 { 285 short console = isatty(fileno(stderr)); 286 287 fprintf(stderr, console ? "\r\n%s: " : "%s: ", name); 288 if (printer) 289 fprintf(stderr, "%s: ", printer); 290 fprintf(stderr, msg, a1, a2, a3); 291 if (console) 292 putc('\r', stderr); 293 putc('\n', stderr); 294 fflush(stderr); 295 } 296 297 logerror(msg) 298 char *msg; 299 { 300 register int err = errno; 301 short console = isatty(fileno(stderr)); 302 extern int sys_nerr; 303 extern char *sys_errlist[]; 304 305 fprintf(stderr, console ? "\r\n%s: " : "%s: ", name); 306 if (*msg) 307 fprintf(stderr, "%s: ", msg); 308 fputs(err < sys_nerr ? sys_errlist[err] : "Unknown error" , stderr); 309 if (console) 310 putc('\r', stderr); 311 putc('\n', stderr); 312 fflush(stderr); 313 } 314