1/*
2 * "$Id: testlpd.c 11560 2014-02-06 20:10:19Z msweet $"
3 *
4 * cups-lpd test program for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 2006 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file.  If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 */
15
16/*
17 * Include necessary headers...
18 */
19
20#include <cups/cups.h>
21#include <cups/string-private.h>
22#include <sys/stat.h>
23#include <sys/wait.h>
24#include <signal.h>
25#include <unistd.h>
26#include <fcntl.h>
27
28
29/*
30 * Local functions...
31 */
32
33static int	do_command(int outfd, int infd, const char *command);
34static int	print_job(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
35static int	print_waiting(int outfd, int infd, char *dest);
36static int	remove_job(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
37static int	status_long(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
38static int	status_short(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
39static void	usage(void) __attribute__((noreturn));
40
41
42/*
43 * 'main()' - Simulate an LPD client.
44 */
45
46int					/* O - Exit status */
47main(int  argc,				/* I - Number of command-line arguments */
48     char *argv[])			/* I - Command-line arguments */
49{
50  int	i;				/* Looping var */
51  int	status;				/* Test status */
52  char	*op,				/* Operation to test */
53	**opargs,			/* Remaining arguments */
54	*dest;				/* Destination */
55  int	cupslpd_argc;			/* Argument count for cups-lpd */
56  char	*cupslpd_argv[1000];		/* Arguments for cups-lpd */
57  int	cupslpd_stdin[2],		/* Standard input for cups-lpd */
58	cupslpd_stdout[2],		/* Standard output for cups-lpd */
59	cupslpd_pid,			/* Process ID for cups-lpd */
60	cupslpd_status;			/* Status of cups-lpd process */
61
62
63 /*
64  * Collect command-line arguments...
65  */
66
67  op              = NULL;
68  opargs          = argv + argc;
69  dest            = NULL;
70  cupslpd_argc    = 1;
71  cupslpd_argv[0] = (char *)"cups-lpd";
72
73  for (i = 1; i < argc; i ++)
74    if (!strncmp(argv[i], "-o", 2))
75    {
76      cupslpd_argv[cupslpd_argc++] = argv[i];
77
78      if (!argv[i][2])
79      {
80        i ++;
81
82	if (i >= argc)
83	  usage();
84
85	cupslpd_argv[cupslpd_argc++] = argv[i];
86      }
87    }
88    else if (argv[i][0] == '-')
89      usage();
90    else if (!op)
91      op = argv[i];
92    else if (!dest)
93      dest = argv[i];
94    else
95    {
96      opargs = argv + i;
97      break;
98    }
99
100  if (!op ||
101      (!strcmp(op, "print-job") && (!dest || !opargs)) ||
102      (!strcmp(op, "remove-job") && (!dest || !opargs)) ||
103      (strcmp(op, "print-job") && strcmp(op, "print-waiting") &&
104       strcmp(op, "remove-job") && strcmp(op, "status-long") &&
105       strcmp(op, "status-short")))
106  {
107    printf("op=\"%s\", dest=\"%s\", opargs=%p\n", op, dest, opargs);
108    usage();
109  }
110
111 /*
112  * Run the cups-lpd program using pipes...
113  */
114
115  cupslpd_argv[cupslpd_argc] = NULL;
116
117  pipe(cupslpd_stdin);
118  pipe(cupslpd_stdout);
119
120  if ((cupslpd_pid = fork()) < 0)
121  {
122   /*
123    * Error!
124    */
125
126    perror("testlpd: Unable to fork");
127    return (1);
128  }
129  else if (cupslpd_pid == 0)
130  {
131   /*
132    * Child goes here...
133    */
134
135    dup2(cupslpd_stdin[0], 0);
136    close(cupslpd_stdin[0]);
137    close(cupslpd_stdin[1]);
138
139    dup2(cupslpd_stdout[1], 1);
140    close(cupslpd_stdout[0]);
141    close(cupslpd_stdout[1]);
142
143    execv("./cups-lpd", cupslpd_argv);
144
145    perror("testlpd: Unable to exec ./cups-lpd");
146    exit(errno);
147  }
148  else
149  {
150    close(cupslpd_stdin[0]);
151    close(cupslpd_stdout[1]);
152  }
153
154 /*
155  * Do the operation test...
156  */
157
158  if (!strcmp(op, "print-job"))
159    status = print_job(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
160  else if (!strcmp(op, "print-waiting"))
161    status = print_waiting(cupslpd_stdin[1], cupslpd_stdout[0], dest);
162  else if (!strcmp(op, "remove-job"))
163    status = remove_job(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
164  else if (!strcmp(op, "status-long"))
165    status = status_long(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
166  else if (!strcmp(op, "status-short"))
167    status = status_short(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
168  else
169  {
170    printf("Unknown operation \"%s\"!\n", op);
171    status = 1;
172  }
173
174 /*
175  * Kill the test program...
176  */
177
178  close(cupslpd_stdin[1]);
179  close(cupslpd_stdout[0]);
180
181  while (wait(&cupslpd_status) != cupslpd_pid);
182
183  printf("cups-lpd exit status was %d...\n", cupslpd_status);
184
185 /*
186  * Return the test status...
187  */
188
189  return (status);
190}
191
192
193/*
194 * 'do_command()' - Send the LPD command and wait for a response.
195 */
196
197static int				/* O - Status from cups-lpd */
198do_command(int        outfd,		/* I - Command file descriptor */
199           int        infd,		/* I - Response file descriptor */
200	   const char *command)		/* I - Command line to send */
201{
202  size_t	len;			/* Length of command line */
203  char		status;			/* Status byte */
204
205
206  printf("COMMAND: %02X %s", command[0], command + 1);
207
208  len = strlen(command);
209
210  if ((size_t)write(outfd, command, len) < len)
211  {
212    puts("    Write failed!");
213    return (-1);
214  }
215
216  if (read(infd, &status, 1) < 1)
217    puts("STATUS: ERROR");
218  else
219    printf("STATUS: %d\n", status);
220
221  return (status);
222}
223
224
225/*
226 * 'print_job()' - Submit a file for printing.
227 */
228
229static int				/* O - Status from cups-lpd */
230print_job(int  outfd,			/* I - Command file descriptor */
231          int  infd,			/* I - Response file descriptor */
232	  char *dest,			/* I - Destination */
233	  char **args)			/* I - Arguments */
234{
235  int		fd;			/* Print file descriptor */
236  char		command[1024],		/* Command buffer */
237		control[1024],		/* Control file */
238		buffer[8192];		/* Print buffer */
239  int		status;			/* Status of command */
240  struct stat	fileinfo;		/* File information */
241  char		*jobname;		/* Job name */
242  int		sequence;		/* Sequence number */
243  ssize_t	bytes;			/* Bytes read/written */
244
245
246 /*
247  * Check the print file...
248  */
249
250  if (stat(args[0], &fileinfo))
251  {
252    perror(args[0]);
253    return (-1);
254  }
255
256  if ((fd = open(args[0], O_RDONLY)) < 0)
257  {
258    perror(args[0]);
259    return (-1);
260  }
261
262 /*
263  * Send the "receive print job" command...
264  */
265
266  snprintf(command, sizeof(command), "\002%s\n", dest);
267  if ((status = do_command(outfd, infd, command)) != 0)
268  {
269    close(fd);
270    return (status);
271  }
272
273 /*
274  * Format a control file string that will be used to submit the job...
275  */
276
277  if ((jobname = strrchr(args[0], '/')) != NULL)
278    jobname ++;
279  else
280    jobname = args[0];
281
282  sequence = (int)getpid() % 1000;
283
284  snprintf(control, sizeof(control),
285           "Hlocalhost\n"
286           "P%s\n"
287           "J%s\n"
288           "ldfA%03dlocalhost\n"
289           "UdfA%03dlocalhost\n"
290           "N%s\n",
291	   cupsUser(), jobname, sequence, sequence, jobname);
292
293 /*
294  * Send the control file...
295  */
296
297  bytes = (ssize_t)strlen(control);
298
299  snprintf(command, sizeof(command), "\002%d cfA%03dlocalhost\n",
300           (int)bytes, sequence);
301
302  if ((status = do_command(outfd, infd, command)) != 0)
303  {
304    close(fd);
305    return (status);
306  }
307
308  bytes ++;
309
310  if (write(outfd, control, (size_t)bytes) < bytes)
311  {
312    printf("CONTROL: Unable to write %d bytes!\n", (int)bytes);
313    close(fd);
314    return (-1);
315  }
316
317  printf("CONTROL: Wrote %d bytes.\n", (int)bytes);
318
319  if (read(infd, command, 1) < 1)
320  {
321    puts("STATUS: ERROR");
322    close(fd);
323    return (-1);
324  }
325  else
326  {
327    status = command[0];
328
329    printf("STATUS: %d\n", status);
330  }
331
332 /*
333  * Send the data file...
334  */
335
336  snprintf(command, sizeof(command), "\003%d dfA%03dlocalhost\n",
337           (int)fileinfo.st_size, sequence);
338
339  if ((status = do_command(outfd, infd, command)) != 0)
340  {
341    close(fd);
342    return (status);
343  }
344
345  while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
346  {
347    if (write(outfd, buffer, (size_t)bytes) < bytes)
348    {
349      printf("DATA: Unable to write %d bytes!\n", (int)bytes);
350      close(fd);
351      return (-1);
352    }
353  }
354
355  write(outfd, "", 1);
356
357  close(fd);
358
359  printf("DATA: Wrote %d bytes.\n", (int)fileinfo.st_size);
360
361  if (read(infd, command, 1) < 1)
362  {
363    puts("STATUS: ERROR");
364    close(fd);
365    return (-1);
366  }
367  else
368  {
369    status = command[0];
370
371    printf("STATUS: %d\n", status);
372  }
373
374  return (status);
375}
376
377
378/*
379 * 'print_waiting()' - Print waiting jobs.
380 */
381
382static int				/* O - Status from cups-lpd */
383print_waiting(int  outfd,		/* I - Command file descriptor */
384              int  infd,		/* I - Response file descriptor */
385	      char *dest)		/* I - Destination */
386{
387  char		command[1024];		/* Command buffer */
388
389
390 /*
391  * Send the "print waiting jobs" command...
392  */
393
394  snprintf(command, sizeof(command), "\001%s\n", dest);
395
396  return (do_command(outfd, infd, command));
397}
398
399
400/*
401 * 'remove_job()' - Cancel a print job.
402 */
403
404static int				/* O - Status from cups-lpd */
405remove_job(int  outfd,			/* I - Command file descriptor */
406           int  infd,			/* I - Response file descriptor */
407	   char *dest,			/* I - Destination */
408	   char **args)			/* I - Arguments */
409{
410  int		i;			/* Looping var */
411  char		command[1024];		/* Command buffer */
412
413 /*
414  * Send the "remove jobs" command...
415  */
416
417  snprintf(command, sizeof(command), "\005%s", dest);
418
419  for (i = 0; args[i]; i ++)
420  {
421    strlcat(command, " ", sizeof(command));
422    strlcat(command, args[i], sizeof(command));
423  }
424
425  strlcat(command, "\n", sizeof(command));
426
427  return (do_command(outfd, infd, command));
428}
429
430
431/*
432 * 'status_long()' - Show the long printer status.
433 */
434
435static int				/* O - Status from cups-lpd */
436status_long(int  outfd,			/* I - Command file descriptor */
437            int  infd,			/* I - Response file descriptor */
438	    char *dest,			/* I - Destination */
439	    char **args)		/* I - Arguments */
440{
441  char		command[1024],		/* Command buffer */
442		buffer[8192];		/* Status buffer */
443  ssize_t	bytes;			/* Bytes read/written */
444
445
446 /*
447  * Send the "send short status" command...
448  */
449
450  if (args)
451    snprintf(command, sizeof(command), "\004%s %s\n", dest, args[0]);
452  else
453    snprintf(command, sizeof(command), "\004%s\n", dest);
454
455  bytes = (ssize_t)strlen(command);
456
457  if (write(outfd, command, (size_t)bytes) < bytes)
458    return (-1);
459
460 /*
461  * Read the status back...
462  */
463
464  while ((bytes = read(infd, buffer, sizeof(buffer))) > 0)
465  {
466    fwrite(buffer, 1, (size_t)bytes, stdout);
467    fflush(stdout);
468  }
469
470  return (0);
471}
472
473
474/*
475 * 'status_short()' - Show the short printer status.
476 */
477
478static int				/* O - Status from cups-lpd */
479status_short(int  outfd,		/* I - Command file descriptor */
480             int  infd,			/* I - Response file descriptor */
481	     char *dest,		/* I - Destination */
482	     char **args)		/* I - Arguments */
483{
484  char		command[1024],		/* Command buffer */
485		buffer[8192];		/* Status buffer */
486  ssize_t	bytes;			/* Bytes read/written */
487
488
489 /*
490  * Send the "send short status" command...
491  */
492
493  if (args)
494    snprintf(command, sizeof(command), "\003%s %s\n", dest, args[0]);
495  else
496    snprintf(command, sizeof(command), "\003%s\n", dest);
497
498  bytes = (ssize_t)strlen(command);
499
500  if (write(outfd, command, (size_t)bytes) < bytes)
501    return (-1);
502
503 /*
504  * Read the status back...
505  */
506
507  while ((bytes = read(infd, buffer, sizeof(buffer))) > 0)
508  {
509    fwrite(buffer, 1, (size_t)bytes, stdout);
510    fflush(stdout);
511  }
512
513  return (0);
514}
515
516
517/*
518 * 'usage()' - Show program usage...
519 */
520
521static void
522usage(void)
523{
524  puts("Usage: testlpd [options] print-job printer filename [... filename]");
525  puts("       testlpd [options] print-waiting [printer or user]");
526  puts("       testlpd [options] remove-job printer [user [job-id]]");
527  puts("       testlpd [options] status-long [printer or user]");
528  puts("       testlpd [options] status-short [printer or user]");
529  puts("");
530  puts("Options:");
531  puts("    -o name=value");
532
533  exit(0);
534}
535
536
537/*
538 * End of "$Id: testlpd.c 11560 2014-02-06 20:10:19Z msweet $".
539 */
540