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