You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
529 lines
11 KiB
529 lines
11 KiB
/*
|
|
* 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);
|
|
}
|