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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <string.h>
32#include <stdarg.h>
33#include <fcntl.h>
34#include <syslog.h>
35#include <errno.h>
36#include <pwd.h>
37#include <libintl.h>
38#include <netdb.h>	/* for rcmd() */
39
40#include <ns.h>
41#include <list.h>
42
43/*  escaped chars include delimiters and shell meta characters */
44#define	ESCAPE_CHARS	"\\\n=: `&;|>^$()<*?["
45
46/*
47 * This modules contains all of the code nedessary to write back to each
48 * printing configuration data repository.  The support is intended to
49 * introduce the least number of dependencies in the library, so it doesn't
50 * always perform it's operations in the cleanest fashion.
51 */
52
53
54/*
55 * Generic Files support begins here.
56 */
57static char *
58freadline(FILE *fp, char *buf, int buflen)
59{
60	char *s = buf;
61
62	while (fgets(s, buflen, fp)) {
63		if ((s == buf) && ((*s == '#') || (*s == '\n'))) {
64			continue;
65		} else {
66			if ((*s == '#') || (*s == '\n')) {
67				*s = NULL;
68				break;
69			}
70
71			buflen -= strlen(s);
72			s += strlen(s);
73
74			if (*(s - 2) != '\\')
75				break;
76#ifdef STRIP_CONTINUATION
77			buflen -= 2;
78			s -= 2;
79#endif
80		}
81	}
82
83	if (s == buf)
84		return (NULL);
85	else
86		return (buf);
87}
88
89
90static int
91_file_put_printer(const char *file, const ns_printer_t *printer)
92{
93	FILE	*ifp,
94	    *ofp;
95	char *tmpfile;
96	int fd;
97	int exit_status = 0;
98	int size;
99
100	size = strlen(file) + 1 + 20;
101	if ((tmpfile = malloc(size)) == NULL)
102		return (-1);
103
104	if (snprintf(tmpfile, size, "%sXXXXXX", file) >= size) {
105		syslog(LOG_ERR, "_file_put_printer:buffer overflow:tmpfile");
106		return (-1);
107	}
108
109	/* LINTED */
110	while (1) {	/* syncronize writes */
111		fd = open(file, O_RDWR|O_CREAT|O_EXCL, 0644);
112		if ((fd < 0) && (errno == EEXIST))
113			fd = open(file, O_RDWR);
114		if (fd < 0) {
115			if (errno == EAGAIN)
116				continue;
117			free(tmpfile);
118			return (-1);
119		}
120		if (lockf(fd, F_TLOCK, 0) == 0)
121			break;
122		(void) close(fd);
123	}
124
125	if ((ifp = fdopen(fd, "r")) == NULL) {
126		(void) close(fd);
127		free(tmpfile);
128		return (-1);
129	}
130
131	if ((fd = mkstemp(tmpfile)) < 0) {
132		(void) fclose(ifp);
133		free(tmpfile);
134		return (-1);
135	}
136
137	(void) fchmod(fd, 0644);
138	if ((ofp = fdopen(fd, "wb+")) != NULL) {
139		char buf[4096];
140
141		(void) fprintf(ofp,
142	"#\n#\tIf you hand edit this file, comments and structure may change.\n"
143	"#\tThe preferred method of modifying this file is through the use of\n"
144	"#\tlpset(1M)\n#\n");
145
146	/*
147	 * Handle the special case of lpset -x all
148	 * This deletes all entries in the file
149	 * In this case, just don't write any entries to the tmpfile
150	 */
151
152		if (!((strcmp(printer->name, "all") == 0) &&
153		    (printer->attributes == NULL))) {
154			char *t, *entry, *pentry;
155
156			(void) _cvt_printer_to_entry((ns_printer_t *)printer,
157			    buf, sizeof (buf));
158			t = pentry = strdup(buf);
159
160			while (freadline(ifp, buf, sizeof (buf)) != NULL) {
161				ns_printer_t *tmp = (ns_printer_t *)
162				    _cvt_nss_entry_to_printer(buf, "");
163
164				if (ns_printer_match_name(tmp, printer->name)
165				    == 0) {
166					entry = pentry;
167					pentry = NULL;
168				} else
169					entry = buf;
170
171				(void) fprintf(ofp, "%s\n", entry);
172			}
173
174			if (pentry != NULL)
175				(void) fprintf(ofp, "%s\n", pentry);
176			free(t);
177		}
178
179		(void) fclose(ofp);
180		(void) rename(tmpfile, file);
181	} else {
182		(void) close(fd);
183		(void) unlink(tmpfile);
184		exit_status = -1;
185	}
186
187	(void) fclose(ifp);	/* releases the lock, after rename on purpose */
188	(void) free(tmpfile);
189	return (exit_status);
190}
191
192
193/*
194 * Support for writing a printer into the FILES /etc/printers.conf
195 * file.
196 */
197int
198files_put_printer(const ns_printer_t *printer)
199{
200	static char *file = "/etc/printers.conf";
201
202	return (_file_put_printer(file, printer));
203}
204
205/*
206 * Support for writing a printer into the NIS printers.conf.byname
207 * map.
208 */
209
210#include <rpc/rpc.h>
211#include <rpcsvc/ypclnt.h>
212#include <rpcsvc/yp_prot.h>
213
214/*
215 * Run the remote command.  We aren't interested in any io, Only the
216 * return code.
217 */
218static int
219remote_command(char *command, char *host)
220{
221	struct passwd *pw;
222
223	if ((pw = getpwuid(getuid())) != NULL) {
224		int fd;
225
226		if ((fd = rcmd_af(&host, htons(514), pw->pw_name, "root",
227		    command, NULL, AF_INET6)) < 0)
228			return (-1);
229		(void) close(fd);
230		return (0);
231	} else
232		return (-1);
233}
234
235
236/*
237 * This isn't all that pretty, but you can update NIS if the machine this
238 * runs on is in the /.rhosts or /etc/hosts.equiv on the NIS master.
239 *   copy it local, update it, copy it remote
240 */
241#define	TMP_PRINTERS_FILE	"/tmp/printers.NIS"
242#define	NIS_MAKEFILE		"/var/yp/Makefile"
243#define	MAKE_EXCERPT		"/usr/lib/print/Makefile.yp"
244/*ARGSUSED*/
245int
246nis_put_printer(const ns_printer_t *printer)
247{
248	static char	*domain = NULL;
249	char *map = "printers.conf.byname";
250	char *tmp = NULL;
251	char *host = NULL;
252	char lfile[BUFSIZ];
253	char rfile[BUFSIZ];
254	char cmd[BUFSIZ];
255
256	if (domain == NULL)
257		(void) yp_get_default_domain(&domain);
258
259	if ((yp_master(domain, (char *)map, &host) != 0) &&
260	    (yp_master(domain, "passwd.byname", &host) != 0))
261		return (-1);
262
263	if (snprintf(lfile, sizeof (lfile), "/tmp/%s", map) >=
264	    sizeof (lfile)) {
265		syslog(LOG_ERR, "nis_put_printer:lfile buffer overflow");
266		return (-1);
267	}
268	if (snprintf(rfile, sizeof (rfile), "root@%s:/etc/%s", host, map) >=
269	    sizeof (rfile)) {
270		syslog(LOG_ERR, "nis_put_printer:rfile buffer overflow");
271		return (-1);
272	}
273
274	if (((tmp = strrchr(rfile, '.')) != NULL) &&
275	    (strcmp(tmp, ".byname") == 0))
276		*tmp = NULL;	/* strip the .byname */
277
278	/* copy it local */
279	if (snprintf(cmd, sizeof (cmd), "rcp %s %s >/dev/null 2>&1",
280	    rfile, lfile) >= sizeof (cmd)) {
281		syslog(LOG_ERR,
282		    "nis_put_printer:buffer overflow building cmd");
283		return (-1);
284	}
285	(void) system(cmd);	/* could fail because it doesn't exist */
286
287
288	/* update it */
289	if (_file_put_printer(lfile, printer) != 0)
290		return (-1);
291
292	/* copy it back */
293	if (snprintf(cmd, sizeof (cmd), "rcp %s %s >/dev/null 2>&1",
294	    lfile, rfile) >= sizeof (cmd)) {
295		syslog(LOG_ERR,
296		    "nis_put_printer:buffer overflow building cmd");
297		return (-1);
298	}
299	if (system(cmd) != 0)
300		return (-1);
301
302	/* copy the Makefile excerpt */
303	if (snprintf(cmd, sizeof (cmd),
304	    "rcp %s root@%s:%s.print >/dev/null 2>&1",
305	    MAKE_EXCERPT, host, NIS_MAKEFILE) >= sizeof (cmd)) {
306		syslog(LOG_ERR,
307		    "nis_put_printer:buffer overflow building cmd");
308		return (-1);
309	}
310
311	if (system(cmd) != 0)
312		return (-1);
313
314	/* run the make */
315	if (snprintf(cmd, sizeof (cmd),
316	    "/bin/sh -c 'PATH=/usr/ccs/bin:/bin:/usr/bin:$PATH "
317	    "make -f %s -f %s.print printers.conf >/dev/null 2>&1'",
318	    NIS_MAKEFILE, NIS_MAKEFILE) >= sizeof (cmd)) {
319		syslog(LOG_ERR,
320		    "nis_put_printer:buffer overflow on make");
321		return (-1);
322	}
323
324	return (remote_command(cmd, host));
325}
326