nss_write.c revision 86b1a8babc969ccc87e5a1c1326fa681d2afe11e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <pwd.h>
#include <libintl.h>
#include <netdb.h> /* for rcmd() */
#include <print/ns.h>
#include <print/list.h>
#include <print/misc.h>
/* escaped chars include delimiters and shell meta characters */
#define ESCAPE_CHARS "\\\n=: `&;|>^$()<*?["
/*
* This modules contains all of the code nedessary to write back to each
* printing configuration data repository. The support is intended to
* introduce the least number of dependencies in the library, so it doesn't
* always perform it's operations in the cleanest fashion.
*/
/*
* Generic Files support begins here.
*/
static char *
freadline(FILE *fp, char *buf, int buflen)
{
char *s = buf;
while (fgets(s, buflen, fp)) {
if ((s == buf) && ((*s == '#') || (*s == '\n'))) {
continue;
} else {
if ((*s == '#') || (*s == '\n')) {
*s = NULL;
break;
}
buflen -= strlen(s);
s += strlen(s);
if (*(s - 2) != '\\')
break;
#ifdef STRIP_CONTINUATION
buflen -= 2;
s -= 2;
#endif
}
}
if (s == buf)
return (NULL);
else
return (buf);
}
static int
_file_put_printer(const char *file, const ns_printer_t *printer)
{
FILE *ifp,
*ofp;
char *tmpfile;
int fd;
int exit_status = 0;
int size;
size = strlen(file) + 1 + 20;
if ((tmpfile = malloc(size)) == NULL)
return (-1);
if (snprintf(tmpfile, size, "%sXXXXXX", file) >= size) {
syslog(LOG_ERR, "_file_put_printer:buffer overflow:tmpfile");
return (-1);
}
/* LINTED */
while (1) { /* syncronize writes */
fd = open(file, O_RDWR|O_CREAT|O_EXCL, 0644);
if ((fd < 0) && (errno == EEXIST))
fd = open(file, O_RDWR);
if (fd < 0) {
if (errno == EAGAIN)
continue;
free(tmpfile);
return (-1);
}
if (lockf(fd, F_TLOCK, 0) == 0)
break;
(void) close(fd);
}
if ((ifp = fdopen(fd, "r")) == NULL) {
(void) close(fd);
free(tmpfile);
return (-1);
}
if ((fd = mkstemp(tmpfile)) < 0) {
(void) fclose(ifp);
free(tmpfile);
return (-1);
}
(void) fchmod(fd, 0644);
if ((ofp = fdopen(fd, "wb+")) != NULL) {
char buf[4096];
(void) fprintf(ofp,
"#\n#\tIf you hand edit this file, comments and structure may change.\n"
"#\tThe preferred method of modifying this file is through the use of\n"
"#\tlpset(1M)\n#\n");
/*
* Handle the special case of lpset -x all
* This deletes all entries in the file
* In this case, just don't write any entries to the tmpfile
*/
if (!((strcmp(printer->name, "all") == 0) &&
(printer->attributes == NULL))) {
char *t, *entry, *pentry;
(void) _cvt_printer_to_entry((ns_printer_t *)printer,
buf, sizeof (buf));
t = pentry = strdup(buf);
while (freadline(ifp, buf, sizeof (buf)) != NULL) {
ns_printer_t *tmp = (ns_printer_t *)
_cvt_nss_entry_to_printer(buf, "");
if (ns_printer_match_name(tmp, printer->name)
== 0) {
entry = pentry;
pentry = NULL;
} else
entry = buf;
(void) fprintf(ofp, "%s\n", entry);
}
if (pentry != NULL)
(void) fprintf(ofp, "%s\n", pentry);
free(t);
}
(void) fclose(ofp);
(void) rename(tmpfile, file);
} else {
(void) close(fd);
(void) unlink(tmpfile);
exit_status = -1;
}
(void) fclose(ifp); /* releases the lock, after rename on purpose */
(void) free(tmpfile);
return (exit_status);
}
/*
* Support for writing a printer into the FILES /etc/printers.conf
* file.
*/
int
files_put_printer(const ns_printer_t *printer)
{
static char *file = "/etc/printers.conf";
return (_file_put_printer(file, printer));
}
/*
* Support for writing a printer into the NIS printers.conf.byname
* map.
*/
#include <rpc/rpc.h>
#include <rpcsvc/ypclnt.h>
#include <rpcsvc/yp_prot.h>
/*
* Run the remote command. We aren't interested in any io, Only the
* return code.
*/
static int
remote_command(char *command, char *host)
{
struct passwd *pw;
if ((pw = getpwuid(getuid())) != NULL) {
int fd;
if ((fd = rcmd_af(&host, htons(514), pw->pw_name, "root",
command, NULL, AF_INET6)) < 0)
return (-1);
(void) close(fd);
return (0);
} else
return (-1);
}
/*
* This isn't all that pretty, but you can update NIS if the machine this
* runs on is in the /.rhosts or /etc/hosts.equiv on the NIS master.
* copy it local, update it, copy it remote
*/
#define TMP_PRINTERS_FILE "/tmp/printers.NIS"
#define NIS_MAKEFILE "/var/yp/Makefile"
#define MAKE_EXCERPT "/usr/lib/print/Makefile.yp"
/*ARGSUSED*/
int
nis_put_printer(const ns_printer_t *printer)
{
static char *domain = NULL;
char *map = "printers.conf.byname";
char *tmp = NULL;
char *host = NULL;
char lfile[BUFSIZ];
char rfile[BUFSIZ];
char cmd[BUFSIZ];
if (domain == NULL)
(void) yp_get_default_domain(&domain);
if ((yp_master(domain, (char *)map, &host) != 0) &&
(yp_master(domain, "passwd.byname", &host) != 0))
return (-1);
if (snprintf(lfile, sizeof (lfile), "/tmp/%s", map) >=
sizeof (lfile)) {
syslog(LOG_ERR, "nis_put_printer:lfile buffer overflow");
return (-1);
}
if (snprintf(rfile, sizeof (rfile), "root@%s:/etc/%s", host, map) >=
sizeof (rfile)) {
syslog(LOG_ERR, "nis_put_printer:rfile buffer overflow");
return (-1);
}
if (((tmp = strrchr(rfile, '.')) != NULL) &&
(strcmp(tmp, ".byname") == 0))
*tmp = NULL; /* strip the .byname */
/* copy it local */
if (snprintf(cmd, sizeof (cmd), "rcp %s %s >/dev/null 2>&1",
rfile, lfile) >= sizeof (cmd)) {
syslog(LOG_ERR,
"nis_put_printer:buffer overflow building cmd");
return (-1);
}
(void) system(cmd); /* could fail because it doesn't exist */
/* update it */
if (_file_put_printer(lfile, printer) != 0)
return (-1);
/* copy it back */
if (snprintf(cmd, sizeof (cmd), "rcp %s %s >/dev/null 2>&1",
lfile, rfile) >= sizeof (cmd)) {
syslog(LOG_ERR,
"nis_put_printer:buffer overflow building cmd");
return (-1);
}
if (system(cmd) != 0)
return (-1);
/* copy the Makefile excerpt */
if (snprintf(cmd, sizeof (cmd),
"rcp %s root@%s:%s.print >/dev/null 2>&1",
MAKE_EXCERPT, host, NIS_MAKEFILE) >= sizeof (cmd)) {
syslog(LOG_ERR,
"nis_put_printer:buffer overflow building cmd");
return (-1);
}
if (system(cmd) != 0)
return (-1);
/* run the make */
if (snprintf(cmd, sizeof (cmd),
"/bin/sh -c 'PATH=/usr/ccs/bin:/bin:/usr/bin:$PATH "
"make -f %s -f %s.print printers.conf >/dev/null 2>&1'",
NIS_MAKEFILE, NIS_MAKEFILE) >= sizeof (cmd)) {
syslog(LOG_ERR,
"nis_put_printer:buffer overflow on make");
return (-1);
}
return (remote_command(cmd, host));
}
/*
* Support for writing a printer into the NISPLUS org_dir.printers table
* begins here. This support uses the nisplus(5) commands rather than the
* nisplus API. This was done to remove the dependency in libprint on the
* API, which is used for lookup in a configuration dependent manner.
*/
#define NISPLUS_CREATE "/usr/bin/nistest -t T printers.org_dir || "\
"( /usr/bin/nistbladm "\
"-D access=og=rmcd,nw=r:group=admin."\
"`/usr/bin/nisdefaults -d` "\
"-c printers_tbl key=S,nogw= datum=,nogw= "\
"printers.org_dir.`/usr/bin/nisdefaults -d` )"
#define NISPLUS_REMOVE "/usr/bin/nistbladm -R key=%s printers.org_dir"
#define NISPLUS_UPDATE "/usr/bin/nistbladm -A key=%s datum="
int
nisplus_put_printer(const ns_printer_t *printer)
{
int rc = 0;
char cmd[BUFSIZ];
if (printer == NULL)
return (rc);
/* create the table if it doesn't exist */
(void) system(NISPLUS_CREATE);
if (printer->attributes != NULL) {
int len;
ns_kvp_t **kvp;
if (snprintf(cmd, sizeof (cmd), NISPLUS_UPDATE,
printer->name) >= sizeof (cmd)) {
syslog(LOG_ERR,
"nisplus_put_printer:NISPLUS_UPDATE:buffer overflow");
return (-1);
}
len = strlen(cmd);
/* Append key/value pairs */
for (kvp = printer->attributes; *kvp != NULL; kvp++)
if (((*kvp)->key != NULL) && ((*kvp)->value != NULL)) {
(void) strlcat(cmd, ":", sizeof (cmd));
(void) strncat_escaped(cmd, (*kvp)->key, sizeof (cmd),
ESCAPE_CHARS);
(void) strlcat(cmd, "=", sizeof (cmd));
(void) strncat_escaped(cmd, (*kvp)->value,
sizeof (cmd), ESCAPE_CHARS);
}
if (len != strlen(cmd))
(void) strlcat(cmd, " printers.org_dir", sizeof (cmd));
else
(void) snprintf(cmd, sizeof (cmd), NISPLUS_REMOVE,
printer->name);
} else
(void) snprintf(cmd, sizeof (cmd), NISPLUS_REMOVE,
printer->name);
if (strlcat(cmd, " >/dev/null 2>&1", sizeof (cmd)) >= sizeof (cmd)) {
syslog(LOG_ERR, "nisplus_put_printer: buffer overflow");
return (-1);
}
/* add/modify/delete the entry */
rc = system(cmd);
return (rc);
}