/* nwjpmv.c Copyright (C) 2003 by Bruno Browning This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Revision history: 0.00 1996 Volker Lendecke Initial revision (as pserver.c) 2003 Bruno Browning nwpjmv.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include "private/libintl.h" #define _(X) gettext(X) struct nw_queue { struct ncp_conn *conn; char queue_name[NCP_BINDERY_NAME_LEN]; u_int32_t queue_id; u_int16_t job_type; char *command; }; static volatile int term_request; static char *progname; static void usage(void) { fprintf(stderr, _("usage: %s [options] file\n"), progname); } static void help(void) { printf(_("\n" "usage: %s [options]\n"), progname); printf(_("\n" "-S server Server name to be used\n" "-U username Print Server name sent to server\n" "-P password Use this password\n" "-n Do not use any password\n" "-C Don't convert password to uppercase\n" "-q queue name Name of the printing queue to use\n" "-c command Name of print command, default: 'lpr'\n" "-j job number ID number of job to service\n" "-d Debug" "\n")); } #ifndef NCP_BINDERY_PSERVER #define NCP_BINDERY_PSERVER (0x0007) #endif static void terminate_handler(int dummy __attribute__((unused))) { signal(SIGTERM, terminate_handler); signal(SIGINT, terminate_handler); term_request = 1; } static int init_queue(struct ncp_conn *conn, char *queue_name, char *command, struct nw_queue *q) { struct ncp_bindery_object obj; long err; str_upper(queue_name); q->conn = conn; q->command = command; err = ncp_get_bindery_object_id(conn, NCP_BINDERY_PQUEUE, queue_name, &obj); if (err) { fprintf(stderr, _("Queue %s not found: %s\n"), queue_name, strnwerror(err)); return -1; } q->queue_id = obj.object_id; memcpy(q->queue_name, obj.object_name, sizeof(q->queue_name)); err = ncp_attach_to_queue(conn, q->queue_id); if (err) { fprintf(stderr, _("Could not attach to queue %s: %s\n"), queue_name, strnwerror(err)); return -1; } return 0; } static char* add_string(char *target, char *target_end, const char *str) { size_t len = strlen(str); if (target + len + 1 > target_end) { len = target_end - target - 1; } memcpy(target, str, len); return target + len; } static void build_command(struct nw_queue *q, struct queue_job *j, char *target, int target_size, char *user) { char *s = q->command; char *target_end = target + target_size; memset(target, 0, target_size); while ((*s != 0) && (target < target_end)) { if (*s != '%') { *target++ = *s++; continue; } switch (*(s + 1)) { case '%': *target = '%'; target += 1; break; case 'u': target = add_string(target, target_end, user); break; case 'd': if (j->j.JobTextDescription[0]) target = add_string(target, target_end, j->j.JobTextDescription); else target = add_string(target, target_end, _("No Description")); break; default: *target = '%'; *(target + 1) = *(s + 1); target += 2; } s += 2; } } static int move_job(struct nw_queue *q, u_int32_t jobID) { struct queue_job job; int fd[2]; int pid; int retcode; struct ncp_bindery_object u; char user[50]; retcode = ncp_change_job_position(q->conn, q->queue_id, jobID, 1); if (retcode != 0) { syslog(LOG_ERR, _("Failed to change job position: %s\n"), strnwerror(retcode)); return -1; } retcode = ncp_service_queue_job(q->conn, q->queue_id, q->job_type, &job); if (retcode != 0) { if (retcode == NWE_Q_NO_JOB || retcode == NWE_SERVER_FAILURE) { /* No job for us */ return 0; } syslog(LOG_ERR, _("Cannot service print job: %s\n"), strnwerror(retcode)); return 2; } if (ncp_get_queue_job_info(q->conn, q->queue_id, job.j.JobNumber, &job.j) != 0) { job.j.JobTextDescription[0]=0; } if (job.j.JobNumber != jobID) { syslog(LOG_ERR, _("wrong job queued: expected %08X, got %08X\n"), jobID, job.j.JobNumber); goto fail; } if ((retcode=ncp_get_bindery_object_name (q->conn, htonl(job.j.ClientObjectID), &u)) == 0) { memcpy(user,u.object_name,48); user[48]=0; } else { sprintf(user,_("")); } if (pipe(fd) < 0) { syslog(LOG_ERR, _("pipe error: %m")); goto fail; } if ((pid = fork()) < 0) { syslog(LOG_ERR, _("fork error: %m")); goto fail; } if (pid > 0) { /* parent */ char buf[1024]; size_t result; off_t offset = 0; close(fd[0]); /* close read end */ while ((result = ncp_read(q->conn, job.file_handle, offset, sizeof(buf), buf)) > 0) { offset += result; if (write(fd[1], buf, result) != (int)result) { goto fail; } } close(fd[1]); /* and close write end */ if (waitpid(pid, NULL, 0) < 0) { syslog(LOG_ERR, _("waitpid: %m\n")); } } else { /* child */ char command[2048]; close(fd[1]); /* close write end */ if (fd[0] != STDIN_FILENO) { if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { syslog(LOG_ERR, _("dup2 error: %m\n")); close(fd[0]); exit(1); } close(fd[0]); } build_command(q, &job, command, sizeof(command), user); execl("/bin/sh", "sh", "-c", command, NULL); syslog(LOG_ERR, _("exec error: %m\n")); close(fd[0]); exit(1); } ncp_finish_servicing_job(q->conn, q->queue_id, job.j.JobNumber, 0); return 1; fail: ncp_abort_servicing_job(q->conn, q->queue_id, job.j.JobNumber); /* We tell that we did not have a job to avoid overloading when something's wrong */ return 0; } static struct nw_queue q; int main(int argc, char *argv[]) { struct ncp_conn *conn; int opt; int debug = 0; int i; long err; char *queue_name = NULL; char *job_num = NULL; char *end; u_int32_t jobID; char default_command[] = "lpr"; char *command = default_command; setlocale(LC_ALL, ""); bindtextdomain(NCPFS_PACKAGE, LOCALEDIR); textdomain(NCPFS_PACKAGE); progname = argv[0]; for (i = 1; i < argc; i += 1) { if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "-?") == 0)) { help(); exit(0); } } for (i = 1; i < argc; i += 1) { if (strcmp(argv[i], "-d") == 0) { debug = 1; break; } } if (debug == 0) { openlog("nwpjmv", LOG_PID, LOG_LPR); } if ((conn = ncp_initialize_as(&argc, argv, 1, NCP_BINDERY_PSERVER, &err)) == NULL) { com_err(argv[0], err, _("when initializing")); return 1; } while ((opt = getopt(argc, argv, "q:c:j:dh")) != EOF) { switch (opt) { case 'q': queue_name = optarg; break; case 'c': command = optarg; break; case 'j': job_num = optarg; break; case 'd': debug = 1; break; case 'h': break; default: usage(); return -1; } } if (argc != optind) { usage(); return -1; } memset(&q, 0, sizeof(q)); if (queue_name == NULL) { fprintf(stderr, _("You must specify a queue\n")); return 1; } if (init_queue(conn, queue_name, command, &q) != 0) { ncp_close(conn); return 1; } term_request = 0; signal(SIGTERM, terminate_handler); signal(SIGINT, terminate_handler); jobID = strtoul(job_num, &end, 16); if (*end) { fprintf(stderr, _("Cannot parse \"%s\" - jobID must be hexadecimal number\n"), argv[i]); } else { int err2; err2 = move_job(&q, jobID); if (err2 != 1) { fprintf(stderr, _("Could not move job %08X\n"), jobID); } } ncp_detach_from_queue(conn, q.queue_id); ncp_close(conn); return 0; }