📄 printd.c
字号:
/* * Print server daemon. */#include "apue.h"#include "print.h"#include "ipp.h"#include <fcntl.h>#include <dirent.h>#include <ctype.h>#include <pwd.h>#include <pthread.h>#include <strings.h>#include <sys/select.h>#include <sys/uio.h>/* * These are for the HTTP response from the printer. */#define HTTP_INFO(x) ((x) >= 100 && (x) <= 199)#define HTTP_SUCCESS(x) ((x) >= 200 && (x) <= 299)/* * Describes a print job. */struct job { struct job *next; /* next in list */ struct job *prev; /* previous in list */ long jobid; /* job ID */ struct printreq req; /* copy of print request */};/* * Describes a thread processing a client request. */struct worker_thread { struct worker_thread *next; /* next in list */ struct worker_thread *prev; /* previous in list */ pthread_t tid; /* thread ID */ int sockfd; /* socket */};/* * Needed for logging. */int log_to_stderr = 0;/* * Printer-related stuff. */struct addrinfo *printer;char *printer_name;pthread_mutex_t configlock = PTHREAD_MUTEX_INITIALIZER;int reread;/* * Thread-related stuff. */struct worker_thread *workers;pthread_mutex_t workerlock = PTHREAD_MUTEX_INITIALIZER;sigset_t mask;/* * Job-related stuff. */struct job *jobhead, *jobtail;int jobfd;long nextjob;pthread_mutex_t joblock = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t jobwait = PTHREAD_COND_INITIALIZER;/* * Function prototypes. */void init_request(void);void init_printer(void);void update_jobno(void);long get_newjobno(void);void add_job(struct printreq *, long);void replace_job(struct job *);void remove_job(struct job *);void build_qonstart(void);void *client_thread(void *);void *printer_thread(void *);void *signal_thread(void *);ssize_t readmore(int, char **, int, int *);int printer_status(int, struct job *);void add_worker(pthread_t, int);void kill_workers(void);void client_cleanup(void *);/* * Main print server thread. Accepts connect requests from * clients and spawns additional threads to service requests. * * LOCKING: none. */intmain(int argc, char *argv[]){ pthread_t tid; struct addrinfo *ailist, *aip; int sockfd, err, i, n, maxfd; char *host; fd_set rendezvous, rset; struct sigaction sa; struct passwd *pwdp; if (argc != 1) err_quit("usage: printd"); daemonize("printd"); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &sa, NULL) < 0) log_sys("sigaction failed"); sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGTERM); if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != 0) log_sys("pthread_sigmask failed"); init_request(); init_printer();#ifdef _SC_HOST_NAME_MAX n = sysconf(_SC_HOST_NAME_MAX); if (n < 0) /* best guess */#endif n = HOST_NAME_MAX; if ((host = malloc(n)) == NULL) log_sys("malloc error"); if (gethostname(host, n) < 0) log_sys("gethostname error"); if ((err = getaddrlist(host, "print", &ailist)) != 0) { log_quit("getaddrinfo error: %s", gai_strerror(err)); exit(1); } FD_ZERO(&rendezvous); maxfd = -1; for (aip = ailist; aip != NULL; aip = aip->ai_next) { if ((sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, QLEN)) >= 0) { FD_SET(sockfd, &rendezvous); if (sockfd > maxfd) maxfd = sockfd; } } if (maxfd == -1) log_quit("service not enabled"); pwdp = getpwnam("lp"); if (pwdp == NULL) log_sys("can't find user lp"); if (pwdp->pw_uid == 0) log_quit("user lp is privileged"); if (setuid(pwdp->pw_uid) < 0) log_sys("can't change IDs to user lp"); pthread_create(&tid, NULL, printer_thread, NULL); pthread_create(&tid, NULL, signal_thread, NULL); build_qonstart(); log_msg("daemon initialized"); for (;;) { rset = rendezvous; if (select(maxfd+1, &rset, NULL, NULL, NULL) < 0) log_sys("select failed"); for (i = 0; i <= maxfd; i++) { if (FD_ISSET(i, &rset)) { /* * Accept the connection and handle * the request. */ sockfd = accept(i, NULL, NULL); if (sockfd < 0) log_ret("accept failed"); pthread_create(&tid, NULL, client_thread, (void *)sockfd); } } } exit(1);}/* * Initialize the job ID file. Use a record lock to prevent * more than one printer daemon from running at a time. * * LOCKING: none, except for record-lock on job ID file. */voidinit_request(void){ int n; char name[FILENMSZ]; sprintf(name, "%s/%s", SPOOLDIR, JOBFILE); jobfd = open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); if (write_lock(jobfd, 0, SEEK_SET, 0) < 0) log_quit("daemon already running"); /* * Reuse the name buffer for the job counter. */ if ((n = read(jobfd, name, FILENMSZ)) < 0) log_sys("can't read job file"); if (n == 0) nextjob = 1; else nextjob = atol(name);}/* * Initialize printer information. * * LOCKING: none. */voidinit_printer(void){ printer = get_printaddr(); if (printer == NULL) { log_msg("no printer device registered"); exit(1); } printer_name = printer->ai_canonname; if (printer_name == NULL) printer_name = "printer"; log_msg("printer is %s", printer_name);}/* * Update the job ID file with the next job number. * * LOCKING: none. */voidupdate_jobno(void){ char buf[32]; lseek(jobfd, 0, SEEK_SET); sprintf(buf, "%ld", nextjob); if (write(jobfd, buf, strlen(buf)) < 0) log_sys("can't update job file");}/* * Get the next job number. * * LOCKING: acquires and releases joblock. */longget_newjobno(void){ long jobid; pthread_mutex_lock(&joblock); jobid = nextjob++; if (nextjob <= 0) nextjob = 1; pthread_mutex_unlock(&joblock); return(jobid);}/* * Add a new job to the list of pending jobs. Then signal * the printer thread that a job is pending. * * LOCKING: acquires and releases joblock. */voidadd_job(struct printreq *reqp, long jobid){ struct job *jp; if ((jp = malloc(sizeof(struct job))) == NULL) log_sys("malloc failed"); memcpy(&jp->req, reqp, sizeof(struct printreq)); jp->jobid = jobid; jp->next = NULL; pthread_mutex_lock(&joblock); jp->prev = jobtail; if (jobtail == NULL) jobhead = jp; else jobtail->next = jp; jobtail = jp; pthread_mutex_unlock(&joblock); pthread_cond_signal(&jobwait);}/* * Replace a job back on the head of the list. * * LOCKING: acquires and releases joblock. */voidreplace_job(struct job *jp){ pthread_mutex_lock(&joblock); jp->prev = NULL; jp->next = jobhead; if (jobhead == NULL) jobtail = jp; else jobhead->prev = jp; jobhead = jp; pthread_mutex_unlock(&joblock);}/* * Remove a job from the list of pending jobs. * * LOCKING: caller must hold joblock. */voidremove_job(struct job *target){ if (target->next != NULL) target->next->prev = target->prev; else jobtail = target->prev; if (target->prev != NULL) target->prev->next = target->next; else jobhead = target->next;}/* * Check the spool directory for pending jobs on start-up. * * LOCKING: none. */voidbuild_qonstart(void){ int fd, err, nr; long jobid; DIR *dirp; struct dirent *entp; struct printreq req; char dname[FILENMSZ], fname[FILENMSZ]; sprintf(dname, "%s/%s", SPOOLDIR, REQDIR); if ((dirp = opendir(dname)) == NULL) return; while ((entp = readdir(dirp)) != NULL) { /* * Skip "." and ".." */ if (strcmp(entp->d_name, ".") == 0 || strcmp(entp->d_name, "..") == 0) continue; /* * Read the request structure. */ sprintf(fname, "%s/%s/%s", SPOOLDIR, REQDIR, entp->d_name); if ((fd = open(fname, O_RDONLY)) < 0) continue; nr = read(fd, &req, sizeof(struct printreq)); if (nr != sizeof(struct printreq)) { if (nr < 0) err = errno; else err = EIO; close(fd); log_msg("build_qonstart: can't read %s: %s", fname, strerror(err)); unlink(fname); sprintf(fname, "%s/%s/%s", SPOOLDIR, DATADIR, entp->d_name); unlink(fname); continue; } jobid = atol(entp->d_name); log_msg("adding job %ld to queue", jobid); add_job(&req, jobid); } closedir(dirp);}/* * Accept a print job from a client. * * LOCKING: none. */void *client_thread(void *arg){ int n, fd, sockfd, nr, nw, first; long jobid; pthread_t tid; struct printreq req; struct printresp res; char name[FILENMSZ]; char buf[IOBUFSZ]; tid = pthread_self(); pthread_cleanup_push(client_cleanup, (void *)tid); sockfd = (int)arg; add_worker(tid, sockfd); /* * Read the request header. */ if ((n = treadn(sockfd, &req, sizeof(struct printreq), 10)) != sizeof(struct printreq)) { res.jobid = 0; if (n < 0) res.retcode = htonl(errno); else res.retcode = htonl(EIO); strncpy(res.msg, strerror(res.retcode), MSGLEN_MAX); writen(sockfd, &res, sizeof(struct printresp)); pthread_exit((void *)1); } req.size = ntohl(req.size); req.flags = ntohl(req.flags); /* * Create the data file. */ jobid = get_newjobno(); sprintf(name, "%s/%s/%ld", SPOOLDIR, DATADIR, jobid); if ((fd = creat(name, FILEPERM)) < 0) { res.jobid = 0; if (n < 0) res.retcode = htonl(errno); else res.retcode = htonl(EIO); log_msg("client_thread: can't create %s: %s", name, strerror(res.retcode)); strncpy(res.msg, strerror(res.retcode), MSGLEN_MAX); writen(sockfd, &res, sizeof(struct printresp)); pthread_exit((void *)1); } /* * Read the file and store it in the spool directory. */ first = 1; while ((nr = tread(sockfd, buf, IOBUFSZ, 20)) > 0) { if (first) { first = 0; if (strncmp(buf, "%!PS", 4) != 0) req.flags |= PR_TEXT; } nw = write(fd, buf, nr); if (nw != nr) { if (nw < 0) res.retcode = htonl(errno); else res.retcode = htonl(EIO); log_msg("client_thread: can't write %s: %s", name, strerror(res.retcode)); close(fd); strncpy(res.msg, strerror(res.retcode), MSGLEN_MAX); writen(sockfd, &res, sizeof(struct printresp)); unlink(name); pthread_exit((void *)1); } } close(fd); /* * Create the control file. */ sprintf(name, "%s/%s/%ld", SPOOLDIR, REQDIR, jobid); fd = creat(name, FILEPERM); if (fd < 0) { res.jobid = 0; if (n < 0) res.retcode = htonl(errno); else res.retcode = htonl(EIO); log_msg("client_thread: can't create %s: %s", name, strerror(res.retcode)); strncpy(res.msg, strerror(res.retcode), MSGLEN_MAX); writen(sockfd, &res, sizeof(struct printresp)); sprintf(name, "%s/%s/%ld", SPOOLDIR, DATADIR, jobid); unlink(name); pthread_exit((void *)1); } nw = write(fd, &req, sizeof(struct printreq)); if (nw != sizeof(struct printreq)) { res.jobid = 0; if (nw < 0) res.retcode = htonl(errno); else res.retcode = htonl(EIO); log_msg("client_thread: can't write %s: %s", name, strerror(res.retcode)); close(fd); strncpy(res.msg, strerror(res.retcode), MSGLEN_MAX); writen(sockfd, &res, sizeof(struct printresp)); unlink(name);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -