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 */ 60 static char * 61 freadline(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 93 static 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 */ 200 int 201 files_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 */ 222 static int 223 remote_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*/ 249 int 250 nis_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 347 int 348 nisplus_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