📄 printd.c
字号:
sprintf(name, "%s/%s/%ld", SPOOLDIR, DATADIR, jobid); unlink(name); pthread_exit((void *)1); } close(fd); /* * Send response to client. */ res.retcode = 0; res.jobid = htonl(jobid); sprintf(res.msg, "request ID %ld", jobid); writen(sockfd, &res, sizeof(struct printresp)); /* * Notify the printer thread, clean up, and exit. */ log_msg("adding job %ld to queue", jobid); add_job(&req, jobid); pthread_cleanup_pop(1); return((void *)0);}/** Add a worker to the list of worker threads.** LOCKING: acquires and releases workerlock.*/voidadd_worker(pthread_t tid, int sockfd){ struct worker_thread *wtp; if ((wtp = malloc(sizeof(struct worker_thread))) == NULL) { log_ret("add_worker: can't malloc"); pthread_exit((void *)1); } wtp->tid = tid; wtp->sockfd = sockfd; pthread_mutex_lock(&workerlock); wtp->prev = NULL; wtp->next = workers; if (workers == NULL) workers = wtp; else workers->prev = wtp; pthread_mutex_unlock(&workerlock);}/* * Cancel (kill) all outstanding workers. * * LOCKING: acquires and releases workerlock. */voidkill_workers(void){ struct worker_thread *wtp; pthread_mutex_lock(&workerlock); for (wtp = workers; wtp != NULL; wtp = wtp->next) pthread_cancel(wtp->tid); pthread_mutex_unlock(&workerlock);}/* * Cancellation routine for the worker thread. * * LOCKING: acquires and releases workerlock. */voidclient_cleanup(void *arg){ struct worker_thread *wtp; pthread_t tid; tid = (pthread_t)arg; pthread_mutex_lock(&workerlock); for (wtp = workers; wtp != NULL; wtp = wtp->next) { if (wtp->tid == tid) { if (wtp->next != NULL) wtp->next->prev = wtp->prev; if (wtp->prev != NULL) wtp->prev->next = wtp->next; else workers = wtp->next; break; } } pthread_mutex_unlock(&workerlock); if (wtp != NULL) { close(wtp->sockfd); free(wtp); }}/* * Deal with signals. * * LOCKING: acquires and releases configlock. */void *signal_thread(void *arg){ int err, signo; for (;;) { err = sigwait(&mask, &signo); if (err != 0) log_quit("sigwait failed: %s", strerror(err)); switch (signo) { case SIGHUP: /* * Schedule to re-read the configuration file. */ pthread_mutex_lock(&configlock); reread = 1; pthread_mutex_unlock(&configlock); break; case SIGTERM: kill_workers(); log_msg("terminate with signal %s", strsignal(signo)); exit(0); default: kill_workers(); log_quit("unexpected signal %d", signo); } }}/* * Add an option to the IPP header. * * LOCKING: none. */char *add_option(char *cp, int tag, char *optname, char *optval){ int n; union { int16_t s; char c[2]; } u; *cp++ = tag; n = strlen(optname); u.s = htons(n); *cp++ = u.c[0]; *cp++ = u.c[1]; strcpy(cp, optname); cp += n; n = strlen(optval); u.s = htons(n); *cp++ = u.c[0]; *cp++ = u.c[1]; strcpy(cp, optval); return(cp + n);}/* * Single thread to communicate with the printer. * * LOCKING: acquires and releases joblock and configlock. */void *printer_thread(void *arg){ struct job *jp; int hlen, ilen, sockfd, fd, nr, nw; char *icp, *hcp; struct ipp_hdr *hp; struct stat sbuf; struct iovec iov[2]; char name[FILENMSZ]; char hbuf[HBUFSZ]; char ibuf[IBUFSZ]; char buf[IOBUFSZ]; char str[64]; for (;;) { /* * Get a job to print. */ pthread_mutex_lock(&joblock); while (jobhead == NULL) { log_msg("printer_thread: waiting..."); pthread_cond_wait(&jobwait, &joblock); } remove_job(jp = jobhead); log_msg("printer_thread: picked up job %ld", jp->jobid); pthread_mutex_unlock(&joblock); update_jobno(); /* * Check for a change in the config file. */ pthread_mutex_lock(&configlock); if (reread) { freeaddrinfo(printer); printer = NULL; printer_name = NULL; reread = 0; pthread_mutex_unlock(&configlock); init_printer(); } else { pthread_mutex_unlock(&configlock); } /* * Send job to printer. */ sprintf(name, "%s/%s/%ld", SPOOLDIR, DATADIR, jp->jobid); if ((fd = open(name, O_RDONLY)) < 0) { log_msg("job %ld canceled - can't open %s: %s", jp->jobid, name, strerror(errno)); free(jp); continue; } if (fstat(fd, &sbuf) < 0) { log_msg("job %ld canceled - can't fstat %s: %s", jp->jobid, name, strerror(errno)); free(jp); close(fd); continue; } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { log_msg("job %ld deferred - can't create socket: %s", jp->jobid, strerror(errno)); goto defer; } if (connect_retry(sockfd, printer->ai_addr, printer->ai_addrlen) < 0) { log_msg("job %ld deferred - can't contact printer: %s", jp->jobid, strerror(errno)); goto defer; } /* * Set up the IPP header. */ icp = ibuf; hp = (struct ipp_hdr *)icp; hp->major_version = 1; hp->minor_version = 1; hp->operation = htons(OP_PRINT_JOB); hp->request_id = htonl(jp->jobid); icp += offsetof(struct ipp_hdr, attr_group); *icp++ = TAG_OPERATION_ATTR; icp = add_option(icp, TAG_CHARSET, "attributes-charset", "utf-8"); icp = add_option(icp, TAG_NATULANG, "attributes-natural-language", "en-us"); sprintf(str, "http://%s:%d", printer_name, IPP_PORT); icp = add_option(icp, TAG_URI, "printer-uri", str); icp = add_option(icp, TAG_NAMEWOLANG, "requesting-user-name", jp->req.usernm); icp = add_option(icp, TAG_NAMEWOLANG, "job-name", jp->req.jobnm); if (jp->req.flags & PR_TEXT) { icp = add_option(icp, TAG_MIMETYPE, "document-format", "text/plain"); } else { icp = add_option(icp, TAG_MIMETYPE, "document-format", "application/postscript"); } *icp++ = TAG_END_OF_ATTR; ilen = icp - ibuf; /* * Set up the HTTP header. */ hcp = hbuf; sprintf(hcp, "POST /%s/ipp HTTP/1.1\r\n", printer_name); hcp += strlen(hcp); sprintf(hcp, "Content-Length: %ld\r\n", (long)sbuf.st_size + ilen); hcp += strlen(hcp); strcpy(hcp, "Content-Type: application/ipp\r\n"); hcp += strlen(hcp); sprintf(hcp, "Host: %s:%d\r\n", printer_name, IPP_PORT); hcp += strlen(hcp); *hcp++ = '\r'; *hcp++ = '\n'; hlen = hcp - hbuf; /* * Write the headers first. Then send the file. */ iov[0].iov_base = hbuf; iov[0].iov_len = hlen; iov[1].iov_base = ibuf; iov[1].iov_len = ilen; if ((nw = writev(sockfd, iov, 2)) != hlen + ilen) { log_ret("can't write to printer"); goto defer; } while ((nr = read(fd, buf, IOBUFSZ)) > 0) { if ((nw = write(sockfd, buf, nr)) != nr) { if (nw < 0) log_ret("can't write to printer"); else log_msg("short write (%d/%d) to printer", nw, nr); goto defer; } } if (nr < 0) { log_ret("can't read %s", name); goto defer; } /* * Read the response from the printer. */ if (printer_status(sockfd, jp)) { unlink(name); sprintf(name, "%s/%s/%ld", SPOOLDIR, REQDIR, jp->jobid); unlink(name); free(jp); jp = NULL; }defer: close(fd); if (sockfd >= 0) close(sockfd); if (jp != NULL) { replace_job(jp); sleep(60); } }}/* * Read data from the printer, possibly increasing the buffer. * Returns offset of end of data in buffer or -1 on failure. * * LOCKING: none. */ssize_treadmore(int sockfd, char **bpp, int off, int *bszp){ ssize_t nr; char *bp = *bpp; int bsz = *bszp; if (off >= bsz) { bsz += IOBUFSZ; if ((bp = realloc(*bpp, bsz)) == NULL) log_sys("readmore: can't allocate bigger read buffer"); *bszp = bsz; *bpp = bp; } if ((nr = tread(sockfd, &bp[off], bsz-off, 1)) > 0) return(off+nr); else return(-1);}/* * Read and parse the response from the printer. Return 1 * if the request was successful, and 0 otherwise. * * LOCKING: none. */intprinter_status(int sockfd, struct job *jp){ int i, success, code, len, found, bufsz; long jobid; ssize_t nr; char *statcode, *reason, *cp, *contentlen; struct ipp_hdr *hp; char *bp; /* * Read the HTTP header followed by the IPP response header. * They can be returned in multiple read attempts. Use the * Content-Length specifier to determine how much to read. */ success = 0; bufsz = IOBUFSZ; if ((bp = malloc(IOBUFSZ)) == NULL) log_sys("printer_status: can't allocate read buffer"); while ((nr = tread(sockfd, bp, IOBUFSZ, 5)) > 0) { /* * Find the status. Response starts with "HTTP/x.y" * so we can skip the first 8 characters. */ cp = bp + 8; while (isspace((int)*cp)) cp++; statcode = cp; while (isdigit((int)*cp)) cp++; if (cp == statcode) { /* Bad format; log it and move on */ log_msg(bp); } else { *cp++ = '\0'; reason = cp; while (*cp != '\r' && *cp != '\n') cp++; *cp = '\0'; code = atoi(statcode); if (HTTP_INFO(code)) continue; if (!HTTP_SUCCESS(code)) { /* probable error: log it */ bp[nr] = '\0'; log_msg("error: %s", reason); break; } /* * The HTTP request was okay, but we still * need to check the IPP status. First * search for the Content-Length specifier. */ i = cp - bp; for (;;) { while (*cp != 'C' && *cp != 'c' && i < nr) { cp++; i++; } if (i >= nr && /* get more header */ ((nr = readmore(sockfd, &bp, i, &bufsz)) < 0)) goto out; cp = &bp[i]; if (strncasecmp(cp, "Content-Length:", 15) == 0) { cp += 15; while (isspace((int)*cp)) cp++; contentlen = cp; while (isdigit((int)*cp)) cp++; *cp++ = '\0'; i = cp - bp; len = atoi(contentlen); break; } else { cp++; i++; } } if (i >= nr && /* get more header */ ((nr = readmore(sockfd, &bp, i, &bufsz)) < 0)) goto out; cp = &bp[i]; found = 0; while (!found) { /* look for end of HTTP header */ while (i < nr - 2) { if (*cp == '\n' && *(cp + 1) == '\r' && *(cp + 2) == '\n') { found = 1; cp += 3; i += 3; break; } cp++; i++; } if (i >= nr && /* get more header */ ((nr = readmore(sockfd, &bp, i, &bufsz)) < 0)) goto out; cp = &bp[i]; } if (nr - i < len && /* get more header */ ((nr = readmore(sockfd, &bp, i, &bufsz)) < 0)) goto out; cp = &bp[i]; hp = (struct ipp_hdr *)cp; i = ntohs(hp->status); jobid = ntohl(hp->request_id); if (jobid != jp->jobid) { /* * Different jobs. Ignore it. */ log_msg("jobid %ld status code %d", jobid, i); break; } if (STATCLASS_OK(i)) success = 1; break; } }out: free(bp); if (nr < 0) { log_msg("jobid %ld: error reading printer response: %s", jobid, strerror(errno)); } return(success);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -