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