/* * cups-lpd test program for CUPS. * * Copyright 2007-2015 by Apple Inc. * Copyright 2006 by Easy Software Products, all rights reserved. * * Licensed under Apache License v2.0. See the file "LICENSE" for more information. */ /* * Include necessary headers... */ #include #include #include #include #include #include #include /* * Local functions... */ static int do_command(int outfd, int infd, const char *command); static int print_job(int outfd, int infd, char *dest, char **args) _CUPS_NONNULL(4); static int print_waiting(int outfd, int infd, char *dest); static int remove_job(int outfd, int infd, char *dest, char **args) _CUPS_NONNULL(4); static int status_long(int outfd, int infd, char *dest, char **args) _CUPS_NONNULL(4); static int status_short(int outfd, int infd, char *dest, char **args) _CUPS_NONNULL(4); static void usage(void) _CUPS_NORETURN; /* * 'main()' - Simulate an LPD client. */ int /* O - Exit status */ main(int argc, /* I - Number of command-line arguments */ char *argv[]) /* I - Command-line arguments */ { int i; /* Looping var */ int status; /* Test status */ char *op, /* Operation to test */ **opargs, /* Remaining arguments */ *dest; /* Destination */ int cupslpd_argc; /* Argument count for cups-lpd */ char *cupslpd_argv[1000]; /* Arguments for cups-lpd */ int cupslpd_stdin[2], /* Standard input for cups-lpd */ cupslpd_stdout[2], /* Standard output for cups-lpd */ cupslpd_pid, /* Process ID for cups-lpd */ cupslpd_status; /* Status of cups-lpd process */ /* * Collect command-line arguments... */ op = NULL; opargs = argv + argc; dest = NULL; cupslpd_argc = 1; cupslpd_argv[0] = (char *)"cups-lpd"; for (i = 1; i < argc; i ++) if (!strncmp(argv[i], "-o", 2)) { cupslpd_argv[cupslpd_argc++] = argv[i]; if (!argv[i][2]) { i ++; if (i >= argc) usage(); cupslpd_argv[cupslpd_argc++] = argv[i]; } } else if (argv[i][0] == '-') usage(); else if (!op) op = argv[i]; else if (!dest) dest = argv[i]; else { opargs = argv + i; break; } if (!op || (!strcmp(op, "print-job") && (!dest || !opargs)) || (!strcmp(op, "remove-job") && (!dest || !opargs)) || (strcmp(op, "print-job") && strcmp(op, "print-waiting") && strcmp(op, "remove-job") && strcmp(op, "status-long") && strcmp(op, "status-short"))) { printf("op=\"%s\", dest=\"%s\", opargs=%p\n", op, dest, opargs); usage(); } /* * Run the cups-lpd program using pipes... */ cupslpd_argv[cupslpd_argc] = NULL; pipe(cupslpd_stdin); pipe(cupslpd_stdout); if ((cupslpd_pid = fork()) < 0) { /* * Error! */ perror("testlpd: Unable to fork"); return (1); } else if (cupslpd_pid == 0) { /* * Child goes here... */ dup2(cupslpd_stdin[0], 0); close(cupslpd_stdin[0]); close(cupslpd_stdin[1]); dup2(cupslpd_stdout[1], 1); close(cupslpd_stdout[0]); close(cupslpd_stdout[1]); execv("./cups-lpd", cupslpd_argv); perror("testlpd: Unable to exec ./cups-lpd"); exit(errno); } else { close(cupslpd_stdin[0]); close(cupslpd_stdout[1]); } /* * Do the operation test... */ if (!strcmp(op, "print-job")) status = print_job(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs); else if (!strcmp(op, "print-waiting")) status = print_waiting(cupslpd_stdin[1], cupslpd_stdout[0], dest); else if (!strcmp(op, "remove-job")) status = remove_job(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs); else if (!strcmp(op, "status-long")) status = status_long(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs); else if (!strcmp(op, "status-short")) status = status_short(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs); else { printf("Unknown operation \"%s\"!\n", op); status = 1; } /* * Kill the test program... */ close(cupslpd_stdin[1]); close(cupslpd_stdout[0]); while (wait(&cupslpd_status) != cupslpd_pid); printf("cups-lpd exit status was %d...\n", cupslpd_status); /* * Return the test status... */ return (status); } /* * 'do_command()' - Send the LPD command and wait for a response. */ static int /* O - Status from cups-lpd */ do_command(int outfd, /* I - Command file descriptor */ int infd, /* I - Response file descriptor */ const char *command) /* I - Command line to send */ { size_t len; /* Length of command line */ char status; /* Status byte */ printf("COMMAND: %02X %s", command[0], command + 1); len = strlen(command); if ((size_t)write(outfd, command, len) < len) { puts(" Write failed!"); return (-1); } if (read(infd, &status, 1) < 1) puts("STATUS: ERROR"); else printf("STATUS: %d\n", status); return (status); } /* * 'print_job()' - Submit a file for printing. */ static int /* O - Status from cups-lpd */ print_job(int outfd, /* I - Command file descriptor */ int infd, /* I - Response file descriptor */ char *dest, /* I - Destination */ char **args) /* I - Arguments */ { int fd; /* Print file descriptor */ char command[1024], /* Command buffer */ control[1024], /* Control file */ buffer[8192]; /* Print buffer */ int status; /* Status of command */ struct stat fileinfo; /* File information */ char *jobname; /* Job name */ int sequence; /* Sequence number */ ssize_t bytes; /* Bytes read/written */ /* * Check the print file... */ if (stat(args[0], &fileinfo)) { perror(args[0]); return (-1); } if ((fd = open(args[0], O_RDONLY)) < 0) { perror(args[0]); return (-1); } /* * Send the "receive print job" command... */ snprintf(command, sizeof(command), "\002%s\n", dest); if ((status = do_command(outfd, infd, command)) != 0) { close(fd); return (status); } /* * Format a control file string that will be used to submit the job... */ if ((jobname = strrchr(args[0], '/')) != NULL) jobname ++; else jobname = args[0]; sequence = (int)getpid() % 1000; snprintf(control, sizeof(control), "Hlocalhost\n" "P%s\n" "J%s\n" "ldfA%03dlocalhost\n" "UdfA%03dlocalhost\n" "N%s\n", cupsUser(), jobname, sequence, sequence, jobname); /* * Send the control file... */ bytes = (ssize_t)strlen(control); snprintf(command, sizeof(command), "\002%d cfA%03dlocalhost\n", (int)bytes, sequence); if ((status = do_command(outfd, infd, command)) != 0) { close(fd); return (status); } bytes ++; if (write(outfd, control, (size_t)bytes) < bytes) { printf("CONTROL: Unable to write %d bytes!\n", (int)bytes); close(fd); return (-1); } printf("CONTROL: Wrote %d bytes.\n", (int)bytes); if (read(infd, command, 1) < 1) { puts("STATUS: ERROR"); close(fd); return (-1); } else { status = command[0]; printf("STATUS: %d\n", status); } /* * Send the data file... */ snprintf(command, sizeof(command), "\003%d dfA%03dlocalhost\n", (int)fileinfo.st_size, sequence); if ((status = do_command(outfd, infd, command)) != 0) { close(fd); return (status); } while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) { if (write(outfd, buffer, (size_t)bytes) < bytes) { printf("DATA: Unable to write %d bytes!\n", (int)bytes); close(fd); return (-1); } } write(outfd, "", 1); close(fd); printf("DATA: Wrote %d bytes.\n", (int)fileinfo.st_size); if (read(infd, command, 1) < 1) { puts("STATUS: ERROR"); close(fd); return (-1); } else { status = command[0]; printf("STATUS: %d\n", status); } return (status); } /* * 'print_waiting()' - Print waiting jobs. */ static int /* O - Status from cups-lpd */ print_waiting(int outfd, /* I - Command file descriptor */ int infd, /* I - Response file descriptor */ char *dest) /* I - Destination */ { char command[1024]; /* Command buffer */ /* * Send the "print waiting jobs" command... */ snprintf(command, sizeof(command), "\001%s\n", dest); return (do_command(outfd, infd, command)); } /* * 'remove_job()' - Cancel a print job. */ static int /* O - Status from cups-lpd */ remove_job(int outfd, /* I - Command file descriptor */ int infd, /* I - Response file descriptor */ char *dest, /* I - Destination */ char **args) /* I - Arguments */ { int i; /* Looping var */ char command[1024]; /* Command buffer */ /* * Send the "remove jobs" command... */ snprintf(command, sizeof(command), "\005%s", dest); for (i = 0; args[i]; i ++) { strlcat(command, " ", sizeof(command)); strlcat(command, args[i], sizeof(command)); } strlcat(command, "\n", sizeof(command)); return (do_command(outfd, infd, command)); } /* * 'status_long()' - Show the long printer status. */ static int /* O - Status from cups-lpd */ status_long(int outfd, /* I - Command file descriptor */ int infd, /* I - Response file descriptor */ char *dest, /* I - Destination */ char **args) /* I - Arguments */ { char command[1024], /* Command buffer */ buffer[8192]; /* Status buffer */ ssize_t bytes; /* Bytes read/written */ /* * Send the "send short status" command... */ if (args[0]) snprintf(command, sizeof(command), "\004%s %s\n", dest, args[0]); else snprintf(command, sizeof(command), "\004%s\n", dest); bytes = (ssize_t)strlen(command); if (write(outfd, command, (size_t)bytes) < bytes) return (-1); /* * Read the status back... */ while ((bytes = read(infd, buffer, sizeof(buffer))) > 0) { fwrite(buffer, 1, (size_t)bytes, stdout); fflush(stdout); } return (0); } /* * 'status_short()' - Show the short printer status. */ static int /* O - Status from cups-lpd */ status_short(int outfd, /* I - Command file descriptor */ int infd, /* I - Response file descriptor */ char *dest, /* I - Destination */ char **args) /* I - Arguments */ { char command[1024], /* Command buffer */ buffer[8192]; /* Status buffer */ ssize_t bytes; /* Bytes read/written */ /* * Send the "send short status" command... */ if (args[0]) snprintf(command, sizeof(command), "\003%s %s\n", dest, args[0]); else snprintf(command, sizeof(command), "\003%s\n", dest); bytes = (ssize_t)strlen(command); if (write(outfd, command, (size_t)bytes) < bytes) return (-1); /* * Read the status back... */ while ((bytes = read(infd, buffer, sizeof(buffer))) > 0) { fwrite(buffer, 1, (size_t)bytes, stdout); fflush(stdout); } return (0); } /* * 'usage()' - Show program usage... */ static void usage(void) { puts("Usage: testlpd [options] print-job printer filename [... filename]"); puts(" testlpd [options] print-waiting [printer or user]"); puts(" testlpd [options] remove-job printer [user [job-id]]"); puts(" testlpd [options] status-long [printer or user]"); puts(" testlpd [options] status-short [printer or user]"); puts(""); puts("Options:"); puts(" -o name=value"); exit(0); }