| /* |
| * 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 <cups/cups.h> |
| #include <cups/string-private.h> |
| #include <sys/stat.h> |
| #include <sys/wait.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| |
| /* |
| * 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); |
| } |