xref: /illumos-gate/usr/src/cmd/lp/lib/printers/putprinter.c (revision 445f2479fe3d7435daab18bf2cdc310b86cd6738)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */
34 
35 #include "sys/types.h"
36 #include "sys/stat.h"
37 #include "stdio.h"
38 #include "string.h"
39 #include "errno.h"
40 #include "stdlib.h"
41 
42 #include "lp.h"
43 #include "printers.h"
44 
45 #include <unistd.h>
46 #include <sys/wait.h>
47 
48 #define	SHELL "/bin/sh"
49 #define	PPDZIP ".gz"
50 
51 extern struct {
52 	char			*v;
53 	short			len,
54 				okremote;
55 }			prtrheadings[];
56 
57 #if	defined(__STDC__)
58 
59 static void		print_sdn (int, char *, SCALED);
60 static void		print_l (int, char *, char **);
61 static void		print_str (int, char *, char *);
62 
63 #ifdef LP_USE_PAPI_ATTR
64 static int addPrintersPPD(char *name, PRINTER *prbufp);
65 static int copyPPDFile(char *ppd, char *printersPPD);
66 static int unzipPPDFile(char *ppd, char *printersPPD);
67 #endif
68 
69 #else
70 
71 static void		print_sdn(),
72 			print_l(),
73 			print_str();
74 
75 #ifdef LP_USE_PAPI_ATTR
76 static int addPrintersPPD();
77 static int copyPPDFile();
78 static int unzipPPDFile();
79 #endif
80 
81 #endif
82 
83 unsigned long		ignprinter	= 0;
84 int			ppdopt		= 0;
85 
86 /**
87  ** putprinter() - WRITE PRINTER STRUCTURE TO DISK FILES
88  **/
89 
90 int
91 putprinter(char *name, PRINTER *prbufp)
92 {
93 	register char *		path;
94 	register char *		stty;
95 	register char *		speed;
96 
97 	int fdin, fdout;
98 
99 	int			fld;
100 
101 	char			buf[BUFSIZ];
102 
103 	struct stat		statbuf1,
104 				statbuf2;
105 
106 
107 	badprinter = 0;
108 
109 	if (!name || !*name) {
110 		errno = EINVAL;
111 		return (-1);
112 	}
113 
114 	if (STREQU(NAME_ALL, name)) {
115 		errno = EINVAL;
116 		return (-1);
117 	}
118 
119 	/*
120 	 * First go through the structure and see if we have
121 	 * anything strange.
122 	 */
123 	if (!okprinter(name, prbufp, 1)) {
124 		errno = EINVAL;
125 		return (-1);
126 	}
127 
128 	if (!Lp_A_Printers || !Lp_A_Interfaces) {
129 		getadminpaths (LPUSER);
130 		if (!Lp_A_Printers || !Lp_A_Interfaces)
131 			return (0);
132 	}
133 
134 	/*
135 	 * Create the parent directory for this printer
136 	 * if it doesn't yet exist.
137 	 */
138 	if (!(path = getprinterfile(name, (char *)0)))
139 		return (-1);
140 	if (Stat(path, &statbuf1) == 0) {
141 		if (!S_ISDIR(statbuf1.st_mode)) {
142 			Free (path);
143 			errno = ENOTDIR;
144 			return (-1);
145 		}
146 	} else if (errno != ENOENT || mkdir_lpdir(path, MODE_DIR) == -1) {
147 		Free (path);
148 		return (-1);
149 	}
150 	Free (path);
151 
152 	/*
153 	 * Create the copy of the interface program, unless
154 	 * that would be silly or not desired.
155 	 * Conversely, make sure the interface program doesn't
156 	 * exist for a remote printer.
157 	 */
158 	if (prbufp->remote) {
159 		if (!(path = makepath(Lp_A_Interfaces, name, (char *)0)))
160 			return (-1);
161 		(void)rmfile (path);
162 		Free (path);
163 	}
164 	if (prbufp->interface && (ignprinter & BAD_INTERFACE) == 0) {
165 		if (Stat(prbufp->interface, &statbuf1) == -1)
166 			return (-1);
167 		if (!(path = makepath(Lp_A_Interfaces, name, (char *)0)))
168 			return (-1);
169 		if (
170 			Stat(path, &statbuf2) == -1
171 		     || statbuf1.st_dev != statbuf2.st_dev
172 		     || statbuf1.st_ino != statbuf2.st_ino
173 		) {
174 			register int		n;
175 
176 			if ((fdin = open_locked(prbufp->interface, "r", 0)) < 0) {
177 				Free (path);
178 				return (-1);
179 			}
180 			if ((fdout = open_locked(path, "w", MODE_EXEC)) < 0) {
181 				Free (path);
182 				close(fdin);
183 				return (-1);
184 			}
185 			while ((n = read(fdin, buf, BUFSIZ)) > 0)
186 				write (fdout, buf,  n);
187 			close(fdout);
188 			close(fdin);
189 		}
190 		Free (path);
191 	}
192 
193 #ifdef LP_USE_PAPI_ATTR
194 	/*
195 	 * Handle PPD (Postscript Printer Definition) file for printer
196 	 * if this printer has been configured with one
197 	 */
198 	if ((prbufp->ppd != NULL) && (ppdopt))
199 	{
200 		if (addPrintersPPD(name, prbufp) != 0)
201 		{
202 			/* failed to added the printers PPD file */
203 			return (-1);
204 		}
205 	}
206 #endif
207 
208 	/*
209 	 * If this printer is dialed up, remove any baud rates
210 	 * from the stty option list and move the last one to
211 	 * the ".speed" member if the ".speed" member isn't already
212 	 * set. Conversely, if this printer is directly connected,
213 	 * move any value from the ".speed" member to the stty list.
214 	 */
215 
216 	stty = (prbufp->stty? Strdup(prbufp->stty) : 0);
217 	if (prbufp->speed)
218 		speed = Strdup(prbufp->speed);
219 	else
220 		speed = 0;
221 
222 	if (prbufp->dial_info && stty) {
223 		register char		*newstty,
224 					*p,
225 					*q;
226 
227 		register int		len;
228 
229 		if (!(q = newstty = Malloc(strlen(stty) + 1))) {
230 			Free (stty);
231 			errno = ENOMEM;
232 			return (-1);
233 		}
234 		newstty[0] = 0;	/* start with empty copy */
235 
236 		for (
237 			p = strtok(stty, " ");
238 			p;
239 			p = strtok((char *)0, " ")
240 		) {
241 			len = strlen(p);
242 			if (strspn(p, "0123456789") == len) {
243 				/*
244 				 * If "prbufp->speed" isn't set, then
245 				 * use the speed we just found. Don't
246 				 * check "speed", because if more than
247 				 * one speed was given in the list, we
248 				 * want the last one.
249 				 */
250 				if (!prbufp->speed) {
251 					if (speed)
252 						Free (speed);
253 					speed = Strdup(p);
254 				}
255 
256 			} else {
257 				/*
258 				 * Not a speed, so copy it to the
259 				 * new stty string.
260 				 */
261 				if (q != newstty)
262 					*q++ = ' ';
263 				strcpy (q, p);
264 				q += len;
265 			}
266 		}
267 
268 		Free (stty);
269 		stty = newstty;
270 
271 	} else if (!prbufp->dial_info && speed) {
272 		register char		*newstty;
273 
274 		newstty = Malloc(strlen(stty) + 1 + strlen(speed) + 1);
275 		if (!newstty) {
276 			if (stty)
277 				Free (stty);
278 			errno = ENOMEM;
279 			return (-1);
280 		}
281 
282 		if (stty) {
283 			strcpy (newstty, stty);
284 			strcat (newstty, " ");
285 			strcat (newstty, speed);
286 			Free (stty);
287 		} else
288 			strcpy (newstty, speed);
289 		Free (speed);
290 		speed = 0;
291 
292 		stty = newstty;
293 
294 	}
295 
296 	/*
297 	 * Open the configuration file and write out the printer
298 	 * configuration.
299 	 */
300 
301 	if (!(path = getprinterfile(name, CONFIGFILE))) {
302 		if (stty)
303 			Free (stty);
304 		if (speed)
305 			Free (speed);
306 		return (-1);
307 	}
308 	if ((fdout = open_locked(path, "w", MODE_READ)) < 0) {
309 		Free (path);
310 		if (stty)
311 			Free (stty);
312 		if (speed)
313 			Free (speed);
314 		return (-1);
315 	}
316 	Free (path);
317 
318 	errno = 0;
319 	for (fld = 0; fld < PR_MAX; fld++) {
320 		if (prbufp->remote && !prtrheadings[fld].okremote)
321 			continue;
322 
323 		switch (fld) {
324 
325 #define HEAD	prtrheadings[fld].v
326 
327 		case PR_BAN:
328 			{
329 				char *ptr = NAME_ON;
330 
331 				switch (prbufp->banner) {
332 				case BAN_ALWAYS:
333 					ptr = NAME_ON;
334 					break;
335 				case BAN_NEVER:
336 					ptr = NAME_OFF;
337 					break;
338 				case BAN_OPTIONAL:
339 					ptr = NAME_OPTIONAL;
340 					break;
341 				}
342 				(void)fdprintf(fdout, "%s %s\n", HEAD, ptr);
343 			}
344 			break;
345 
346 		case PR_CPI:
347 			print_sdn(fdout, HEAD, prbufp->cpi);
348 			break;
349 
350 		case PR_CS:
351 			if (!emptylist(prbufp->char_sets))
352 				print_l(fdout, HEAD, prbufp->char_sets);
353 			break;
354 
355 		case PR_ITYPES:
356 			/*
357 			 * Put out the header even if the list is empty,
358 			 * to distinguish no input types from the default.
359 			 */
360 			print_l(fdout, HEAD, prbufp->input_types);
361 			break;
362 
363 		case PR_DEV:
364 			print_str(fdout, HEAD, prbufp->device);
365 			break;
366 
367 		case PR_DIAL:
368 			print_str(fdout, HEAD, prbufp->dial_info);
369 			break;
370 
371 		case PR_RECOV:
372 			print_str(fdout, HEAD, prbufp->fault_rec);
373 			break;
374 
375 		case PR_INTFC:
376 			print_str(fdout, HEAD, prbufp->interface);
377 			break;
378 
379 		case PR_LPI:
380 			print_sdn(fdout, HEAD, prbufp->lpi);
381 			break;
382 
383 		case PR_LEN:
384 			print_sdn(fdout, HEAD, prbufp->plen);
385 			break;
386 
387 		case PR_LOGIN:
388 			if (prbufp->login & LOG_IN)
389 				(void)fdprintf(fdout, "%s\n", HEAD);
390 			break;
391 
392 		case PR_PTYPE:
393 		{
394 			char			**printer_types;
395 
396 			/*
397 			 * For backward compatibility for those who
398 			 * use only "->printer_type", we have to play
399 			 * some games here.
400 			 */
401 			if (prbufp->printer_type && !prbufp->printer_types)
402 				printer_types = getlist(
403 					prbufp->printer_type,
404 					LP_WS,
405 					LP_SEP
406 				);
407 			else
408 				printer_types = prbufp->printer_types;
409 
410 			if (!printer_types || !*printer_types)
411 				print_str(fdout, HEAD, NAME_UNKNOWN);
412 			else
413 				print_l(fdout, HEAD, printer_types);
414 
415 			if (printer_types != prbufp->printer_types)
416 				freelist (printer_types);
417 			break;
418 		}
419 
420 		case PR_REMOTE:
421 			print_str(fdout, HEAD, prbufp->remote);
422 			break;
423 
424 		case PR_SPEED:
425 			print_str(fdout, HEAD, speed);
426 			break;
427 
428 		case PR_STTY:
429 			print_str(fdout, HEAD, stty);
430 			break;
431 
432 		case PR_WIDTH:
433 			print_sdn(fdout, HEAD, prbufp->pwid);
434 			break;
435 
436 #if	defined(CAN_DO_MODULES)
437 		case PR_MODULES:
438 			/*
439 			 * Put out the header even if the list is empty,
440 			 * to distinguish no modules from the default.
441 			 */
442 			print_l(fdout, HEAD, prbufp->modules);
443 			break;
444 #endif
445 
446 		case PR_OPTIONS:
447 			print_l(fdout, HEAD, prbufp->options);
448 			break;
449 
450 		case PR_PPD:
451 		{
452 			print_str(fdout, HEAD, prbufp->ppd);
453 			break;
454 		}
455 		}
456 
457 	}
458 	if (stty)
459 		Free (stty);
460 	if (speed)
461 		Free (speed);
462 	if (errno != 0) {
463 		close(fdout);
464 		return (-1);
465 	}
466 	close(fdout);
467 
468 	/*
469 	 * If we have a description of the printer,
470 	 * write it out to a separate file.
471 	 */
472 	if (prbufp->description) {
473 
474 		if (!(path = getprinterfile(name, COMMENTFILE)))
475 			return (-1);
476 
477 		if (dumpstring(path, prbufp->description) == -1) {
478 			Free (path);
479 			return (-1);
480 		}
481 		Free (path);
482 
483 	}
484 
485 	/*
486 	 * Now write out the alert condition.
487 	 */
488 	if (
489 		prbufp->fault_alert.shcmd
490 	     && putalert(Lp_A_Printers, name, &(prbufp->fault_alert)) == -1
491 	)
492 		return (-1);
493 
494 	return (0);
495 }
496 
497 /**
498  ** print_sdn() - PRINT SCALED DECIMAL NUMBER WITH HEADER
499  ** print_l() - PRINT (char **) LIST WITH HEADER
500  ** print_str() - PRINT STRING WITH HEADER
501  **/
502 
503 static void
504 print_sdn(int fd, char *head, SCALED sdn)
505 {
506 	if (sdn.val <= 0)
507 		return;
508 
509 	(void)fdprintf (fd, "%s ", head);
510 	fdprintsdn (fd, sdn);
511 
512 	return;
513 }
514 
515 static void
516 print_l(int fd, char *head, char **list)
517 {
518 	(void)fdprintf (fd, "%s ", head);
519 	printlist_setup (0, 0, LP_SEP, 0);
520 	fdprintlist (fd, list);
521 	printlist_unsetup ();
522 
523 	return;
524 }
525 
526 static void
527 print_str(int fd, char *head, char *str)
528 {
529 	if (!str || !*str)
530 		return;
531 
532 	(void)fdprintf (fd, "%s %s\n", head, str);
533 
534 	return;
535 }
536 
537 
538 #ifdef LP_USE_PAPI_ATTR
539 /*
540  * Function:     addPrintersPPD()
541  *
542  * Description:  Handle PPD (Postscript Printer Definition) file for this
543  *               printer if it has been configured with one
544  *
545  */
546 
547 static int
548 addPrintersPPD(char *name, PRINTER *prbufp)
549 
550 {
551 	int result = 0;
552 	char *path = NULL;
553 	char *ppd = NULL;
554 	char  buf[BUFSIZ];
555 	struct stat statbuf;
556 
557 	(void) snprintf(buf, sizeof (buf), "%s.ppd", name);
558 	if (prbufp->remote)
559 	{
560 		/* make sure the PPD file doesn't exist for a remote printer */
561 		if (!(path = makepath(ETCDIR, "ppd", buf, (char *)0)))
562 		{
563 			result = -1;
564 		}
565 		else
566 		{
567 			(void) rmfile(path);
568 		}
569 	}
570 
571 	if ((result == 0) && (prbufp->ppd != NULL))
572 	{
573 		ppd = strdup(prbufp->ppd);
574 
575 		if (ppd == NULL)
576 		{
577 			result = -1;
578 		}
579 		else
580 		{
581 			/* Check the PPD file given exists */
582 
583 			if (Stat(ppd, &statbuf) == -1)
584 			{
585 				/*
586 				 * The given ppd files does not exist, but
587 				 * check if there is a zipped version of the
588 				 * file that we can use instead
589 				 */
590 				if (strstr(ppd, PPDZIP) != NULL)
591 				{
592 					/* this is a zipped file so exit */
593 					result = -1;
594 				}
595 				else
596 				{
597 					ppd = Realloc(ppd,
598 						strlen(ppd)+strlen(PPDZIP)+2);
599 					if (ppd != NULL)
600 					{
601 						ppd = strcat(ppd, PPDZIP);
602 						if (Stat(ppd, &statbuf) == -1)
603 						{
604 							/*
605 							 * this zipped version
606 							 * of the file does not
607 							 * exist either
608 							 */
609 							result = -1;
610 						}
611 					}
612 					else
613 					{
614 						result = -1;
615 					}
616 				}
617 			}
618 		}
619 
620 		/*
621 		 * Create the copy of the PPD file for this printer
622 		 * unless that would be silly or not desired
623 		 */
624 
625 		if (result == 0)
626 		{
627 			if (!(path = makepath(ETCDIR, "ppd", buf, (char *)0)))
628 			{
629 				result = -1;
630 			}
631 		}
632 
633 		/*
634 		 * At this point we may have a zipped or unzipped ppd file, if
635 		 * it's unzipped just copy it otherwise unzip it to the
636 		 * printer's ppd file (/etc/lp/ppd/<printer>.ppd)
637 		 */
638 
639 		if (result == 0)
640 		{
641 			if (strstr(ppd, PPDZIP) == NULL)
642 			{
643 				result = copyPPDFile(ppd, path);
644 			}
645 			else
646 			{
647 				result = unzipPPDFile(ppd, path);
648 			}
649 
650 			(void) chown_lppath(path);
651 			(void) chmod(path, 0644);
652 		}
653 
654 		if (ppd != NULL)
655 		{
656 			Free(ppd);
657 		}
658 		if (path != NULL)
659 		{
660 			Free(path);
661 		}
662 	}
663 
664 	return (result);
665 } /* addPrintersPPD() */
666 
667 
668 /*
669  * Function:     copyPPDFile()
670  *
671  * Description:  Copy the given ppd file to the printer's file in /etc/lp/ppd
672  *
673  */
674 
675 static int
676 copyPPDFile(char *ppd, char *printersPPD)
677 
678 {
679 	int  result = 0;
680 	register int n = 0;
681 	int  fdin  = 0;
682 	int  fdout = 0;
683 	char buf[BUFSIZ];
684 
685 	if ((ppd != NULL) && (printersPPD != NULL))
686 	{
687 		if ((fdin = open_locked(ppd, "r", 0)) < 0)
688 		{
689 			result = -1;
690 		}
691 		else
692 		{
693 			fdout = open_locked(printersPPD, "w", MODE_EXEC);
694 			if (fdout < 0)
695 			{
696 				close(fdin);
697 				result = -1;
698 			}
699 		}
700 
701 		if (result == 0)
702 		{
703 			while ((n = read(fdin, buf, BUFSIZ)) > 0)
704 			{
705 				write(fdout, buf,  n);
706 			}
707 			close(fdout);
708 			close(fdin);
709 		}
710 	}
711 	else
712 	{
713 		result = -1;
714 	}
715 
716 	return (result);
717 } /* copyPPDFile() */
718 
719 
720 
721 /*
722  * Function:     unzipPPDFile()
723  *
724  * Description:  Unzip the given ppd file to the printer's file in /etc/lp/ppd.
725  *               This is done by forking and running the unzip utility on the
726  *               zipped ppd file.
727  *
728  */
729 
730 static int
731 unzipPPDFile(char *ppd, char *printersPPD)
732 
733 {
734 	int  result = -1;
735 	char *cmdLine = NULL;
736 	pid_t childPID = 0;
737 	int   stat = 0;
738 	int   clSize = 0;
739 
740 
741 	if ((ppd != NULL) && (printersPPD != NULL))
742 	{
743 		childPID = fork();
744 
745 		switch (childPID)
746 		{
747 			case -1:
748 			{
749 				/* return error */
750 				break;
751 			}
752 
753 			case 0:
754 			{
755 				/* child process  - so execute something */
756 
757 				clSize = strlen("/usr/bin/rm -f ") +
758 						strlen(printersPPD) +
759 						strlen("/usr/bin/gzip -dc ") +
760 						strlen(ppd) +
761 						strlen(printersPPD) + 20;
762 				cmdLine = malloc(clSize);
763 				if (cmdLine != NULL)
764 				{
765 
766 					(void) snprintf(cmdLine, clSize,
767 				"/usr/bin/rm -f %s; /usr/bin/gzip -dc %s > %s",
768 							printersPPD, ppd,
769 							printersPPD);
770 					result = execl(SHELL, SHELL, "-c",
771 							cmdLine, NULL);
772 					exit(result);
773 				}
774 				break;
775 			}
776 
777 			default:
778 			{
779 				/* parent process, child pid is in childPID */
780 
781 				while (wait(&stat) != childPID);
782 
783 				if ((stat & 0xff00) == 0)
784 				{
785 					result = 0;
786 				}
787 				break;
788 			}
789 		}
790 	}
791 
792 	return (result);
793 } /* unzipPPDFile() */
794 #endif
795