1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Add TSOL banner, trailer, page header/footers to a print job
31  */
32 
33 /* system header files */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 #include <limits.h>
40 #include <errno.h>
41 #include <signal.h>
42 #include <locale.h>
43 #include <tsol/label.h>
44 
45 /* typedefs */
46 
47 typedef int BOOL;
48 
49 /* constants */
50 
51 #ifndef FALSE
52 #define	FALSE 0
53 #endif
54 #ifndef TRUE
55 #define	TRUE 1
56 #endif
57 
58 #define	ME "lp.tsol_separator"
59 #define	POSTSCRIPTLIB "/usr/lib/lp/postscript"
60 #define	SEPARATORPS "tsol_separator.ps"
61 #define	BANNERPS "tsol_banner.ps"
62 #define	TRAILERPS "tsol_trailer.ps"
63 #define	MAXUSERLEN 32
64 #define	MAXHOSTLEN 32
65 
66 /* external variables */
67 
68 int	optind;			/* Used by getopt */
69 char    *optarg;		/* Used by getopt */
70 
71 /* prototypes for static functions */
72 
73 static int ProcessArgs(int argc, char **argv);
74 static void Usage(void);
75 static void ParseUsername(char *input, char *user, char *host);
76 static void EmitPSFile(const char *name);
77 static BOOL EmitFile(FILE *file);
78 static void EmitJobData(void);
79 static void EmitPrologue(void);
80 static void EmitCommandLineInfo(void);
81 static void EmitClockBasedInfo(void);
82 static void EmitLabelInfo(void);
83 static void CopyStdin(void);
84 
85 /* static variables */
86 
87 static char *ArgSeparatorPS;
88 static char *ArgBannerPS;
89 static char *ArgTrailerPS;
90 static char *ArgPSLib;
91 static char *ArgPrinter;
92 static char *ArgJobID;
93 static char *ArgUser;
94 static char *ArgTitle;
95 static char *ArgFile;
96 static BOOL ArgReverse;
97 static BOOL ArgNoPageLabels;
98 static int ArgDebugLevel;
99 static FILE *ArgLogFile;
100 static m_label_t *FileLabel;
101 static char *remoteLabel;
102 
103 int
main(int argc,char * argv[])104 main(int argc, char *argv[])
105 {
106 	int	err;
107 	/*
108 	 * Run immune from typical interruptions, so that
109 	 * we stand a chance to get the fault message.
110 	 * EOF (or startup error) is the only way out.
111 	 */
112 	(void) signal(SIGHUP, SIG_IGN);
113 	(void) signal(SIGINT, SIG_IGN);
114 	(void) signal(SIGQUIT, SIG_IGN);
115 	(void) signal(SIGTERM, SIG_IGN);
116 
117 	(void) setlocale(LC_ALL, "");
118 #if !defined(TEXT_DOMAIN)
119 #define	TEXT_DOMAIN "SYS_TEST"
120 #endif
121 	(void) textdomain(TEXT_DOMAIN);
122 
123 	if (ProcessArgs(argc, argv) != 0)
124 		exit(1);
125 
126 	if ((FileLabel = m_label_alloc(MAC_LABEL)) == NULL)
127 		exit(1);
128 	/*
129 	 * If the job was submitted via remotely, the label of the
130 	 * remote peer will be set in the SLABEL environment variable
131 	 * by copying it out of the SECURE structure.
132 	 *
133 	 * If there is no SLABEL value, the job was submitted locally
134 	 * via the named pipe, and the file label can be determined
135 	 * from its pathname.
136 	 */
137 	if ((remoteLabel = getenv("SLABEL")) != NULL) {
138 		m_label_free(FileLabel);
139 		FileLabel = NULL;
140 		if (str_to_label(remoteLabel, &FileLabel, MAC_LABEL,
141 		    L_NO_CORRECTION, &err) == -1) {
142 			perror("str_to_label");
143 			exit(1);
144 		}
145 	} else if (getlabel(ArgFile, FileLabel) != 0) {
146 		(void) fprintf(ArgLogFile,
147 		    gettext("%1$s: cannot get label of %2$s: %3$s\n"),
148 		    ME, ArgFile, strerror(errno));
149 		exit(1);
150 	}
151 
152 	/* All of these functions exit if they encounter an error */
153 	EmitJobData();
154 	EmitPSFile(ArgSeparatorPS);
155 	if (ArgReverse)
156 		EmitPSFile(ArgTrailerPS);
157 	else
158 		EmitPSFile(ArgBannerPS);
159 	CopyStdin();
160 	if (ArgReverse)
161 		EmitPSFile(ArgBannerPS);
162 	else
163 		EmitPSFile(ArgTrailerPS);
164 	if (ArgDebugLevel >= 1)
165 		(void) fprintf(ArgLogFile, gettext("Done.\n"));
166 	m_label_free(FileLabel);
167 	return (0);
168 }
169 
170 static void
EmitJobData(void)171 EmitJobData(void)
172 {
173 	EmitPrologue();
174 	EmitCommandLineInfo();
175 	EmitClockBasedInfo();
176 	EmitLabelInfo();
177 
178 	/* Emit ending PostScript code */
179 	(void) printf("end\n\n");
180 	(void) printf("%%%% End of code generated by lp.tsol_separator\n\n");
181 
182 }
183 
184 static void
EmitPrologue(void)185 EmitPrologue(void)
186 {
187 	/* Emit preliminary PostScript code */
188 	(void) printf("%%!\n\n");
189 	(void) printf("%%%% Begin code generated by lp.tsol_separator\n\n");
190 
191 	(void) printf("%%%% Create JobDict if it doesn't exist\n");
192 	(void) printf("userdict /JobDict known not {\n");
193 	(void) printf("  userdict /JobDict 100 dict put\n");
194 	(void) printf("} if\n\n");
195 
196 	(void) printf("%%%% Define job parameters, including TSOL security "
197 	    "info\n");
198 	(void) printf("JobDict\n");
199 	(void) printf("begin\n");
200 }
201 
202 /* Emit parameters obtained from command line options */
203 
204 static void
EmitCommandLineInfo(void)205 EmitCommandLineInfo(void)
206 {
207 	char user[MAXUSERLEN + 1];
208 	char host[MAXHOSTLEN + 1];
209 
210 	(void) printf("\t/Job_Printer (%s) def\n", ArgPrinter);
211 	ParseUsername(ArgUser, user, host);
212 	(void) printf("\t/Job_Host (%s) def\n", host);
213 	(void) printf("\t/Job_User (%s) def\n", user);
214 	(void) printf("\t/Job_JobID (%s) def\n", ArgJobID);
215 	(void) printf("\t/Job_Title (%s) def\n", ArgTitle);
216 	(void) printf("\t/Job_DoPageLabels (%s) def\n",
217 	    ArgNoPageLabels ? "NO" : "YES");
218 	(void) printf("\n");
219 }
220 
221 /* Emit parameters generated from the system clock */
222 
223 static void
EmitClockBasedInfo(void)224 EmitClockBasedInfo(void)
225 {
226 	char timebuf[80];
227 	struct timeval clockval;
228 
229 	(void) gettimeofday(&clockval, NULL);
230 	(void) strftime(timebuf, sizeof (timebuf), NULL,
231 	    localtime(&clockval.tv_sec));
232 	(void) printf("\t/Job_Date (%s) def\n", timebuf);
233 	(void) printf("\t/Job_Hash (%ld) def\n", clockval.tv_usec % 100000L);
234 	(void) printf("\n");
235 }
236 
237 /* Emit parameters derived from the SL and IL of the file being printed. */
238 
239 static void
EmitLabelInfo(void)240 EmitLabelInfo(void)
241 {
242 	char	*header = NULL;		/* DIA banner page fields */
243 	char	*label = NULL;
244 	char	*caveats = NULL;
245 	char	*channels = NULL;
246 	char	*page_label = NULL;	/* interior pages label */
247 
248 	if (label_to_str(FileLabel, &header, PRINTER_TOP_BOTTOM,
249 	    DEF_NAMES) != 0) {
250 		(void) fprintf(ArgLogFile,
251 		    gettext("%s: label_to_str PRINTER_TOP_BOTTOM: %s.\n"),
252 		    ME, strerror(errno));
253 		exit(1);
254 	}
255 	if (label_to_str(FileLabel, &label, PRINTER_LABEL,
256 	    DEF_NAMES) != 0) {
257 		(void) fprintf(ArgLogFile,
258 		    gettext("%s: label_to_str PRINTER_LABEL: %s.\n"),
259 		    ME, strerror(errno));
260 		exit(1);
261 	}
262 	if (label_to_str(FileLabel, &caveats, PRINTER_CAVEATS,
263 	    DEF_NAMES) != 0) {
264 		(void) fprintf(ArgLogFile,
265 		    gettext("%s: label_to_str PRINTER_CAVEATS: %s.\n"),
266 		    ME, strerror(errno));
267 		exit(1);
268 	}
269 	if (label_to_str(FileLabel, &channels, PRINTER_CHANNELS,
270 	    DEF_NAMES) != 0) {
271 		(void) fprintf(ArgLogFile,
272 		    gettext("%s: label_to_str PRINTER_CHANNELS: %s.\n"),
273 		    ME, strerror(errno));
274 		exit(1);
275 	}
276 	if (label_to_str(FileLabel, &page_label, M_LABEL,
277 	    LONG_NAMES) != 0) {
278 		(void) fprintf(ArgLogFile,
279 		    gettext("%s: label_to_str M_LABEL: %s.\n"),
280 		    ME, strerror(errno));
281 		exit(1);
282 	}
283 
284 	(void) printf("\t/Job_Classification (%s) def\n", header);
285 	(void) printf("\t/Job_Protect (%s) def\n", label);
286 	(void) printf("\t/Job_Caveats (%s) def\n", caveats);
287 	(void) printf("\t/Job_Channels (%s) def\n", channels);
288 	(void) printf("\t/Job_SL_Internal (%s) def\n", page_label);
289 
290 	/* Free memory allocated label_to_str */
291 	free(header);
292 	free(label);
293 	free(caveats);
294 	free(channels);
295 	free(page_label);
296 }
297 
298 /*
299  * Parse input "host!user" to separate host and user names.
300  */
301 
302 static void
ParseUsername(char * input,char * user,char * host)303 ParseUsername(char *input, char *user, char *host)
304 {
305 	char *cp;
306 
307 	if ((cp = strchr(input, '@')) != NULL) {
308 		/* user@host */
309 		(void) strlcpy(host, cp + 1, MAXHOSTLEN + 1);
310 		*cp = '\0';
311 		(void) strlcpy(user, input, MAXUSERLEN + 1);
312 		*cp = '@';
313 	} else if ((cp = strchr(input, '!')) != NULL) {
314 		/* host!user */
315 		(void) strlcpy(user, cp + 1, MAXUSERLEN + 1);
316 		*cp = '\0';
317 		(void) strlcpy(host, input, MAXHOSTLEN + 1);
318 		*cp = '!';
319 	} else {
320 		/* user */
321 		(void) strlcpy(user, input, MAXUSERLEN + 1);
322 		host[0] = '\0';
323 	}
324 }
325 
326 
327 static void
CopyStdin(void)328 CopyStdin(void)
329 {
330 	if (!EmitFile(stdin)) {
331 		(void) fprintf(ArgLogFile,
332 		    gettext("%s: Error copying stdin to stdout\n"), ME);
333 		exit(1);
334 	}
335 }
336 
337 
338 static BOOL
EmitFile(FILE * file)339 EmitFile(FILE *file)
340 {
341 	int len;
342 #define	BUFLEN 1024
343 	char buf[BUFLEN];
344 
345 	while ((len = fread(buf, 1, BUFLEN, file)) > 0) {
346 		if (fwrite(buf, 1, len, stdout) != len)
347 			return (FALSE);
348 	}
349 	if (!feof(file))
350 		return (FALSE);
351 	return (TRUE);
352 }
353 
354 
355 static void
EmitPSFile(const char * name)356 EmitPSFile(const char *name)
357 {
358 	char path[PATH_MAX];
359 	FILE *file;
360 	BOOL emitted;
361 
362 	if (name[0] != '/') {
363 		(void) strlcpy(path, ArgPSLib, sizeof (path));
364 		(void) strlcat(path, "/", sizeof (path));
365 		(void) strlcat(path, name, sizeof (path));
366 	} else {
367 		(void) strlcpy(path, name, sizeof (path));
368 	}
369 
370 	file = fopen(path, "r");
371 	if (file == NULL) {
372 		(void) fprintf(ArgLogFile,
373 		    gettext("%s: Error opening PostScript file %s. %s.\n"),
374 		    ME, path, strerror(errno));
375 		exit(1);
376 	}
377 
378 	emitted = EmitFile(file);
379 	(void) fclose(file);
380 	if (!emitted) {
381 		(void) fprintf(ArgLogFile, gettext(
382 		    "%s: Error copying PostScript file %s to stdout.\n"),
383 		    ME, path);
384 		exit(1);
385 	}
386 }
387 
388 
389 static int
ProcessArgs(int argc,char * argv[])390 ProcessArgs(int argc, char *argv[])
391 {
392 	int	option_letter;
393 	char	*options_string = "lrd:e:s:b:t:L:";
394 
395 	/* set default values for arguments */
396 	ArgSeparatorPS = SEPARATORPS;
397 	ArgBannerPS = BANNERPS;
398 	ArgTrailerPS = TRAILERPS;
399 	ArgPSLib = POSTSCRIPTLIB;
400 	ArgNoPageLabels = ArgReverse = FALSE;
401 	ArgDebugLevel = 0;
402 	ArgLogFile = stderr;
403 
404 	/* read switch arguments once to get error log file */
405 	while ((option_letter = getopt(argc, argv, options_string)) != EOF) {
406 		switch (option_letter) {
407 		case 'd':
408 			ArgDebugLevel = atoi(optarg);
409 			break;
410 		case 'e':
411 			ArgLogFile = fopen(optarg, "a");
412 			if (ArgLogFile == NULL) {
413 				(void) fprintf(stderr,
414 				    gettext("Cannot open log file %s\n"),
415 				    optarg);
416 				return (-1);
417 			}
418 			break;
419 		case '?':	/* ? or unrecognized option */
420 			Usage();
421 			return (-1);
422 		}
423 	}
424 
425 	if (ArgDebugLevel > 0)
426 		(void) fprintf(ArgLogFile,
427 		    gettext("Processing switch arguments\n"));
428 
429 	/* re-read switch arguments */
430 	optind = 1;
431 	while ((option_letter = getopt(argc, argv, options_string)) != EOF) {
432 		switch (option_letter) {
433 		case 'd':
434 			ArgDebugLevel = atoi(optarg);
435 			break;
436 		case 'e':
437 			/* This was handled in earlier pass through args */
438 			break;
439 		case 'l':
440 			ArgNoPageLabels = TRUE;
441 			break;
442 		case 'r':
443 			ArgReverse = TRUE;
444 			break;
445 		case 's':
446 			ArgSeparatorPS = optarg;
447 			break;
448 		case 'b':
449 			ArgBannerPS = optarg;
450 			break;
451 		case 't':
452 			ArgTrailerPS = optarg;
453 			break;
454 		case 'L':
455 			ArgPSLib = optarg;
456 			break;
457 		case '?':	/* ? or unrecognized option */
458 			Usage();
459 			return (-1);
460 		}
461 	}
462 
463 	/* Adjust arguments to skip over options */
464 	argc -= optind;		/* Number of remaining(non-switch) args */
465 	argv += optind;		/* argv[0] is first(non-switch) args */
466 
467 	if (argc != 5) {
468 		(void) fprintf(ArgLogFile,
469 		    gettext("Wrong number of arguments.\n\n"));
470 		Usage();
471 		return (-1);
472 	}
473 
474 	ArgPrinter = argv++[0];
475 	ArgJobID = argv++[0];
476 	ArgUser = argv++[0];
477 	ArgTitle = argv++[0];
478 	ArgFile = argv++[0];
479 
480 	if (ArgDebugLevel >= 1) {
481 		(void) fprintf(ArgLogFile, gettext("Arguments processed\n"));
482 		(void) fprintf(ArgLogFile, gettext("Printer: %s\n"),
483 		    ArgPrinter);
484 		(void) fprintf(ArgLogFile, gettext("Job ID: %s\n"), ArgJobID);
485 		(void) fprintf(ArgLogFile, gettext("User: %s\n"), ArgUser);
486 		(void) fprintf(ArgLogFile, gettext("Title: %s\n"), ArgTitle);
487 		(void) fprintf(ArgLogFile, gettext("File: %s\n"), ArgFile);
488 	}
489 
490 	return (0);
491 }
492 
493 
494 static void
Usage(void)495 Usage(void)
496 {
497 	static const char *OPTFMT = "    %-8s %-9s %s\n";
498 
499 	(void) fprintf(ArgLogFile,
500 	    gettext("Usage:  lp.tsol_separator [OPTIONS] %s\n"),
501 	    gettext("PRINTER JOBID HOST!USER TITLE FILE"));
502 	(void) fprintf(ArgLogFile, gettext("  OPTIONS:\n"));
503 	(void) fprintf(ArgLogFile, OPTFMT, "-r", gettext("Reverse"),
504 	    gettext("Reverse banner/trailer order"));
505 	(void) fprintf(ArgLogFile, OPTFMT, "-l", gettext("Labels"),
506 	    gettext("Suppress page header/footer labels"));
507 	(void) fprintf(ArgLogFile, OPTFMT, gettext("-b FILE"),
508 	    gettext("Banner"),
509 	    gettext("PostScript program for banner (default tsol_banner.ps)"));
510 	(void) fprintf(ArgLogFile, OPTFMT, gettext("-s FILE"),
511 	    gettext("Separator"),
512 	    gettext("PostScript program for separator "
513 	    "(default tsol_separator.ps)"));
514 	(void) fprintf(ArgLogFile, OPTFMT, gettext("-t FILE"),
515 	    gettext("Trailer"),
516 	    gettext("PostScript program for trailer "
517 	    "(default tsol_trailer.ps)"));
518 	(void) fprintf(ArgLogFile, OPTFMT, gettext("-L DIR"),
519 	    gettext("Library"),
520 	    gettext("Directory to search for PostScript programs"));
521 	(void) fprintf(ArgLogFile, OPTFMT, "", "",
522 	    gettext("(default /usr/lib/lp/postscript)"));
523 	(void) fprintf(ArgLogFile, OPTFMT, gettext("-d N"), gettext("Debug"),
524 	    gettext("Set debug level to N"));
525 	(void) fprintf(ArgLogFile, OPTFMT, gettext("-e FILE"),
526 	    gettext("Error File"),
527 	    gettext("Append error and debugging output to FILE"));
528 }
529