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