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 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 */ 27 28 /* $Id: nss.c 166 2006-05-20 05:48:55Z njacobs $ */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <ctype.h> 37 #include <sys/types.h> 38 #include <syslog.h> 39 #include <papi.h> 40 #include <uri.h> 41 #include <papi_impl.h> 42 #ifdef NSS_EMULATION 43 #include <nss-emulation.h> 44 #elif NSS_SOLARIS 45 #include <nss_dbdefs.h> 46 #endif 47 #include <config-site.h> 48 #if defined(__sun) && defined(__SVR4) 49 #include <sys/systeminfo.h> 50 #endif 51 52 53 static char * 54 bsdaddr_to_uri(char *bsdaddr) 55 { 56 char *result = NULL; 57 58 if (bsdaddr != NULL) { 59 char *bsd[3], *tmp, *iter = NULL; 60 char buf[512]; 61 62 tmp = strdup(bsdaddr); 63 64 bsd[0] = strtok_r(tmp, ":,", &iter); 65 bsd[1] = strtok_r(NULL, ":,", &iter); 66 bsd[2] = strtok_r(NULL, ":,", &iter); 67 68 snprintf(buf, sizeof (buf), "lpd://%s/%s%s%s", bsd[0], bsd[1], 69 (bsd[2] != NULL) ? "#" : "", 70 (bsd[2] != NULL) ? bsd[2] : ""); 71 72 free(tmp); 73 74 result = strdup(buf); 75 } 76 77 return (result); 78 } 79 80 #if defined(__sun) && defined(__SVR4) 81 #include <sys/socket.h> 82 #include <sys/ioctl.h> 83 #include <sys/sockio.h> 84 #include <net/if.h> 85 #include <netinet/in.h> 86 #include <arpa/inet.h> 87 #include <netdb.h> 88 89 static struct in6_addr ** 90 local_interfaces() 91 { 92 struct in6_addr **result = NULL; 93 int s; 94 struct lifnum n; 95 struct lifconf c; 96 struct lifreq *r; 97 int count; 98 99 /* we need a socket to get the interfaces */ 100 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 101 return (0); 102 103 /* get the number of interfaces */ 104 memset(&n, 0, sizeof (n)); 105 n.lifn_family = AF_UNSPEC; 106 if (ioctl(s, SIOCGLIFNUM, (char *)&n) < 0) { 107 close(s); 108 return (0); /* no interfaces */ 109 } 110 111 /* get the interface(s) configuration */ 112 memset(&c, 0, sizeof (c)); 113 c.lifc_family = AF_UNSPEC; 114 c.lifc_buf = calloc(n.lifn_count, sizeof (struct lifreq)); 115 c.lifc_len = (n.lifn_count * sizeof (struct lifreq)); 116 if (ioctl(s, SIOCGLIFCONF, (char *)&c) < 0) { 117 free(c.lifc_buf); 118 close(s); 119 return (0); /* can't get interface(s) configuration */ 120 } 121 close(s); 122 123 r = c.lifc_req; 124 for (count = c.lifc_len / sizeof (struct lifreq); 125 count > 0; count--, r++) { 126 struct in6_addr v6[1], *addr = NULL; 127 128 switch (r->lifr_addr.ss_family) { 129 case AF_INET: { 130 struct sockaddr_in *s = 131 (struct sockaddr_in *)&r->lifr_addr; 132 IN6_INADDR_TO_V4MAPPED(&s->sin_addr, v6); 133 addr = v6; 134 } 135 break; 136 case AF_INET6: { 137 struct sockaddr_in6 *s = 138 (struct sockaddr_in6 *)&r->lifr_addr; 139 addr = &s->sin6_addr; 140 } 141 break; 142 } 143 144 if (addr != NULL) { 145 struct in6_addr *a = malloc(sizeof (*a)); 146 147 memcpy(a, addr, sizeof (*a)); 148 list_append(&result, a); 149 } 150 } 151 free(c.lifc_buf); 152 153 return (result); 154 } 155 156 static int 157 match_interfaces(char *host) 158 { 159 struct in6_addr **lif = local_interfaces(); 160 struct hostent *hp; 161 int rc = 0; 162 int errnum; 163 164 /* are there any local interfaces */ 165 if (lif == NULL) 166 return (0); 167 168 /* cycle through the host db addresses */ 169 hp = getipnodebyname(host, AF_INET6, AI_ALL|AI_V4MAPPED, &errnum); 170 if (hp != NULL) { 171 struct in6_addr **tmp = (struct in6_addr **)hp->h_addr_list; 172 int i; 173 174 for (i = 0; ((rc == 0) && (tmp[i] != NULL)); i++) { 175 int j; 176 177 for (j = 0; ((rc == 0) && (lif[j] != NULL)); j++) 178 if (memcmp(tmp[i], lif[j], 179 sizeof (struct in6_addr)) == 0) 180 rc = 1; 181 } 182 } 183 free(lif); 184 185 return (rc); 186 } 187 188 static int 189 is_localhost(char *host) 190 { 191 char hostname[BUFSIZ]; 192 193 /* is it "localhost" */ 194 if (strncasecmp(host, "localhost", 10) == 0) 195 return (1); 196 197 /* is it the {nodename} */ 198 sysinfo(SI_HOSTNAME, hostname, sizeof (hostname)); 199 if (strncasecmp(host, hostname, strlen(hostname)) == 0) 200 return (1); 201 202 /* does it match one of the host's configured interfaces */ 203 if (match_interfaces(host) != 0) 204 return (1); 205 206 return (0); 207 } 208 209 /* 210 * This is an awful HACK to force the dynamic PAPI library to use the 211 * lpsched support when the destination apears to be a local lpsched 212 * queue on Solaris. 213 */ 214 static void 215 solaris_lpsched_shortcircuit_hack(papi_attribute_t ***list) 216 { 217 papi_attribute_t *attribute; 218 uri_t *uri = NULL; 219 char *printer = NULL; 220 char buf[128], buf2[128]; 221 222 /* setting this in the calling env can be useful for debugging */ 223 if (getenv("DISABLE_LPSCHED_SHORTCIRCUIT") != NULL) 224 return; 225 226 papiAttributeListGetString(*list, NULL, 227 "printer-uri-supported", &printer); 228 if (uri_from_string(printer, &uri) < 0) 229 return; 230 231 /* already an lpsched URI ? */ 232 if (strcasecmp(uri->scheme, "lpsched") == 0) 233 return; 234 235 if ((printer = strrchr(uri->path, '/')) == NULL) 236 printer = uri->path; 237 else 238 printer++; 239 240 /* is there an lpsched queue (printer/class) */ 241 snprintf(buf, sizeof (buf), "/etc/lp/interfaces/%s", printer); 242 snprintf(buf2, sizeof (buf2), "/etc/lp/classes/%s", printer); 243 if ((access(buf, F_OK) < 0) && (access(buf2, F_OK) < 0)) 244 return; 245 246 /* is this the "local" host */ 247 if ((uri->host != NULL) && (is_localhost(uri->host) == 0)) 248 return; 249 250 snprintf(buf, sizeof (buf), "lpsched://%s/printers/%s", 251 (uri->host ? uri->host : "localhost"), printer); 252 papiAttributeListAddString(list, PAPI_ATTR_REPLACE, 253 "printer-uri-supported", buf); 254 } 255 #endif 256 257 static void 258 fill_printer_uri_supported(papi_attribute_t ***list) 259 { 260 papi_attribute_t *attribute; 261 char *string = NULL; 262 263 /* do we have a printer-uri-supported */ 264 attribute = papiAttributeListFind(*list, "printer-uri-supported"); 265 if (attribute != NULL) /* we have what we need, return */ 266 return; 267 268 /* do we have a printer-uri to rename */ 269 attribute = papiAttributeListFind(*list, "printer-uri"); 270 if (attribute != NULL) { /* rename it in place and return */ 271 free(attribute->name); 272 attribute->name = strdup("printer-uri-supported"); 273 return; 274 } 275 276 /* do we have a printers.conf(4) "bsdaddr" to convert */ 277 papiAttributeListGetString(*list, NULL, "bsdaddr", &string); 278 if (string != NULL) { /* parse it, convert it, add it */ 279 char *uri = bsdaddr_to_uri(string); 280 281 if (uri != NULL) { 282 papiAttributeListAddString(list, PAPI_ATTR_APPEND, 283 "printer-uri-supported", uri); 284 papiAttributeListDelete(list, "bsdaddr"); 285 free(uri); 286 return; 287 } 288 } 289 290 /* do we have a printers.conf(4) "rm" (and "rp") to convert */ 291 papiAttributeListGetString(*list, NULL, "rm", &string); 292 if (string != NULL) { 293 char *rp = NULL; 294 295 /* default to "printer-name", but use "rp" if we have it */ 296 papiAttributeListGetString(*list, NULL, "printer-name", &rp); 297 papiAttributeListGetString(*list, NULL, "rp", &rp); 298 299 if (rp != NULL) { /* fill in the uri if we have the data */ 300 char buf[BUFSIZ]; 301 302 snprintf(buf, sizeof (buf), "lpd://%s/printers/%s", 303 string, rp); 304 papiAttributeListAddString(list, PAPI_ATTR_APPEND, 305 "printer-uri-supported", strdup(buf)); 306 return; 307 } 308 } 309 310 /* if were are here, we don't have a printer-uri-supported */ 311 } 312 313 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC 314 static void 315 fill_printer_uri(papi_attribute_t ***list) 316 { 317 papi_attribute_t *attribute; 318 char *uri = NULL; 319 320 if ((list == NULL) || (*list == NULL)) 321 return; 322 323 /* do we have a printer-uri */ 324 attribute = papiAttributeListFind(*list, "printer-uri"); 325 if (attribute != NULL) /* we have what we need, return */ 326 return; 327 328 /* 329 * this is sufficient to fool libgnomeprintpapi, but not promote it's 330 * use in the future. 331 */ 332 papiAttributeListAddString(list, PAPI_ATTR_EXCL, "printer-uri", 333 "broken printer-uri semantic"); 334 } 335 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */ 336 337 static void 338 cvt_all_to_member_names(papi_attribute_t ***list) 339 { 340 papi_status_t status; 341 void *iter = NULL; 342 char *string = NULL; 343 344 papiAttributeListGetString(*list, NULL, "member-names", &string); 345 if (string != NULL) /* already have a member-names */ 346 return; 347 348 for (status = papiAttributeListGetString(*list, &iter, "all", &string); 349 status == PAPI_OK; 350 status = papiAttributeListGetString(*list, &iter, NULL, &string)) { 351 char *s_iter = NULL, *value, *tmp = strdup(string); 352 353 for (value = strtok_r(tmp, ", \t", &s_iter); 354 value != NULL; 355 value = strtok_r(NULL, ", \t", &s_iter)) 356 papiAttributeListAddString(list, PAPI_ATTR_APPEND, 357 "member-names", value); 358 free(tmp); 359 } 360 } 361 362 static papi_attribute_t ** 363 _cvt_nss_entry_to_printer(char *entry) 364 { 365 char *key = NULL, 366 *cp, 367 buf[BUFSIZ]; 368 int in_namelist = 1, buf_pos = 0; 369 papi_attribute_t **list = NULL; 370 371 if (entry == NULL) 372 return (NULL); 373 374 memset(buf, 0, sizeof (buf)); 375 for (cp = entry; *cp != '\0'; cp++) { 376 switch (*cp) { 377 case ':': /* end of kvp */ 378 if (in_namelist != 0) { 379 papiAttributeListAddString(&list, 380 PAPI_ATTR_APPEND, "printer-name", buf); 381 in_namelist = 0; 382 } else if (key != NULL) 383 papiAttributeListAddString(&list, 384 PAPI_ATTR_APPEND, key, buf); 385 memset(buf, 0, sizeof (buf)); 386 buf_pos = 0; 387 key = NULL; 388 break; 389 case '=': /* kvp seperator */ 390 if (key == NULL) { 391 key = strdup(buf); 392 memset(buf, 0, sizeof (buf)); 393 buf_pos = 0; 394 } else 395 buf[buf_pos++] = *cp; 396 break; 397 case '|': /* namelist seperator */ 398 if (in_namelist != 0) { 399 papiAttributeListAddString(&list, 400 PAPI_ATTR_APPEND, "printer-name", buf); 401 memset(buf, 0, sizeof (buf)); 402 buf_pos = 0; 403 } else /* add it to the buffer */ 404 buf[buf_pos++] = *cp; 405 break; 406 case '\\': /* escape char */ 407 buf[buf_pos++] = *(++cp); 408 break; 409 default: 410 buf[buf_pos++] = *cp; 411 } 412 413 } 414 415 if (key != NULL) 416 papiAttributeListAddString(&list, PAPI_ATTR_APPEND, key, buf); 417 418 /* resolve any "use" references in the configuration DB */ 419 key = NULL; 420 papiAttributeListGetString(list, NULL, "use", &key); 421 if (key != NULL) { 422 papi_attribute_t **use_attrs = getprinterbyname(key, NULL); 423 424 list_concatenate(&list, use_attrs); 425 } 426 427 fill_printer_uri_supported(&list); 428 cvt_all_to_member_names(&list); /* convert "all" to "member-names" */ 429 430 return (list); 431 } 432 433 #if defined(NSS_SOLARIS) && !defined(NSS_EMULATION) 434 435 #ifndef NSS_DBNAM__PRINTERS /* not in nss_dbdefs.h because it's private */ 436 #define NSS_DBNAM__PRINTERS "_printers" 437 #endif 438 439 static DEFINE_NSS_DB_ROOT(db_root); 440 static DEFINE_NSS_GETENT(context); 441 442 static char *private_ns = NULL; 443 static char initialized = 0; 444 445 static void 446 _nss_initf_printers(p) 447 nss_db_params_t *p; 448 { 449 if (private_ns != NULL) { 450 /* 451 * because we need to support a legacy interface that allows 452 * us to select a specific name service, we need to dummy up 453 * the parameters to use a private nsswitch database and set 454 * the * default_config entry to the name service we are 455 * looking into. 456 */ 457 p->name = NSS_DBNAM__PRINTERS; /* "_printers" */ 458 p->default_config = private_ns; 459 private_ns = NULL; 460 } else if (initialized == 0) { 461 /* regular behaviour */ 462 p->name = NSS_DBNAM_PRINTERS; /* "printers" */ 463 p->default_config = NSS_DEFCONF_PRINTERS; 464 initialized = 1; 465 } 466 syslog(LOG_DEBUG, "database: %s, services: %s", 467 (p->name ? p->name : "NULL"), 468 (p->default_config ? p->default_config : "NULL")); 469 } 470 471 /* 472 * Return values: 0 = success, 1 = parse error, 2 = erange ... 473 * The structure pointer passed in is a structure in the caller's space 474 * wherein the field pointers would be set to areas in the buffer if 475 * need be. instring and buffer should be separate areas. 476 */ 477 /* ARGSUSED */ 478 static int 479 str2printer(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 480 { 481 if (lenstr + 1 > buflen) 482 return (NSS_STR_PARSE_ERANGE); 483 /* 484 * We copy the input string into the output buffer 485 */ 486 (void) memcpy(buffer, instr, lenstr); 487 buffer[lenstr] = '\0'; 488 489 return (NSS_STR_PARSE_SUCCESS); 490 } 491 #endif /* NSS_SOLARIS */ 492 493 int 494 setprinterentry(int stayopen, char *ns) 495 { 496 #ifdef NSS_EMULATION 497 emul_setprinterentry(stayopen); 498 #elif NSS_SOLARIS 499 initialized = 0; 500 private_ns = ns; 501 nss_setent(&db_root, _nss_initf_printers, &context); 502 #endif 503 return (0); 504 } 505 506 507 int 508 endprinterentry(int i) 509 { 510 #ifdef NSS_EMULATION 511 emul_endprinterentry(); 512 #elif NSS_SOLARIS 513 initialized = 0; 514 nss_endent(&db_root, _nss_initf_printers, &context); 515 nss_delete(&db_root); 516 #endif 517 return (0); 518 } 519 520 /* ARGSUSED2 */ 521 papi_attribute_t ** 522 getprinterentry(char *ns) 523 { 524 papi_attribute_t **result = NULL; 525 526 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS) 527 char buf[10240]; 528 nss_status_t res = NSS_NOTFOUND; 529 530 #ifdef NSS_EMULATION 531 res = emul_getprinterentry_r(buf, sizeof (buf)); 532 #elif NSS_SOLARIS 533 nss_XbyY_args_t arg; 534 535 NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer); 536 res = nss_getent(&db_root, _nss_initf_printers, &context, &arg); 537 (void) NSS_XbyY_FINI(&arg); 538 #endif 539 540 if (res != NSS_SUCCESS) 541 buf[0] = '\0'; 542 543 result = _cvt_nss_entry_to_printer(buf); 544 #if defined(__sun) && defined(__SVR4) 545 solaris_lpsched_shortcircuit_hack(&result); 546 #endif 547 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC 548 fill_printer_uri(&result); 549 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */ 550 #endif 551 552 #ifdef DEBUG 553 printf("getprinterentry(%s): 0x%8.8x\n", (ns ? ns : "NULL"), result); 554 if (result != NULL) { 555 char buf[4096]; 556 557 papiAttributeListToString(result, "\n\t", buf, sizeof (buf)); 558 printf("\t%s\n", buf); 559 } 560 #endif /* DEBUG */ 561 562 return (result); 563 } 564 565 566 papi_attribute_t ** 567 getprinterbyname(char *name, char *ns) 568 { 569 papi_attribute_t **result = NULL; 570 571 if (strstr(name, "://") != NULL) { /* shortcut for URI form */ 572 papiAttributeListAddString(&result, PAPI_ATTR_APPEND, 573 "printer-name", name); 574 papiAttributeListAddString(&result, PAPI_ATTR_APPEND, 575 "printer-uri-supported", name); 576 } else if (strchr(name, ':') != NULL) { /* shortcut for POSIX form */ 577 char *uri = bsdaddr_to_uri(name); 578 579 papiAttributeListAddString(&result, PAPI_ATTR_APPEND, 580 "printer-name", name); 581 if (uri != NULL) { 582 papiAttributeListAddString(&result, PAPI_ATTR_APPEND, 583 "printer-uri-supported", uri); 584 free(uri); 585 } 586 } else { /* anything else */ 587 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS) 588 char buf[10240]; 589 nss_status_t res = NSS_NOTFOUND; 590 591 #ifdef NSS_EMULATION 592 res = emul_getprinterbyname_r(name, buf, sizeof (buf)); 593 #elif NSS_SOLARIS 594 nss_XbyY_args_t arg; 595 596 private_ns = ns; 597 NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer); 598 arg.key.name = name; 599 res = nss_search(&db_root, _nss_initf_printers, 600 NSS_DBOP_PRINTERS_BYNAME, &arg); 601 (void) NSS_XbyY_FINI(&arg); 602 603 if (res != NSS_SUCCESS) 604 buf[0] = '\0'; 605 #endif 606 607 result = _cvt_nss_entry_to_printer(buf); 608 #endif 609 } 610 #if defined(__sun) && defined(__SVR4) 611 solaris_lpsched_shortcircuit_hack(&result); 612 #endif 613 #ifdef DEBUG 614 printf("getprinterbyname(%s): %s = 0x%8.8x\n", (ns ? ns : "NULL"), 615 name, result); 616 if (result != NULL) { 617 char buf[4096]; 618 619 papiAttributeListToString(result, "\n\t", buf, sizeof (buf)); 620 printf("\t%s\n", buf); 621 } 622 #endif /* DEBUG */ 623 624 return (result); 625 } 626