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