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