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 2008 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 (in URI form) to rename */ 272 attribute = papiAttributeListFind(*list, "printer-uri"); 273 if ((attribute != NULL) && 274 (attribute->type == PAPI_STRING) && 275 (attribute->values != NULL) && 276 (attribute->values[0]->string != NULL) && 277 (strstr(attribute->values[0]->string, "://") != NULL)) { 278 /* rename it in place and return */ 279 free(attribute->name); 280 attribute->name = strdup("printer-uri-supported"); 281 return; 282 } 283 284 /* do we have a printers.conf(4) "bsdaddr" to convert */ 285 papiAttributeListGetString(*list, NULL, "bsdaddr", &string); 286 if (string != NULL) { /* parse it, convert it, add it */ 287 char *uri = bsdaddr_to_uri(*list, string); 288 289 if (uri != NULL) { 290 papiAttributeListAddString(list, PAPI_ATTR_APPEND, 291 "printer-uri-supported", uri); 292 papiAttributeListDelete(list, "bsdaddr"); 293 free(uri); 294 return; 295 } 296 } 297 298 /* do we have a printers.conf(4) "rm" (and "rp") to convert */ 299 papiAttributeListGetString(*list, NULL, "rm", &string); 300 if (string != NULL) { 301 char *rp = NULL; 302 303 /* default to "printer-name", but use "rp" if we have it */ 304 papiAttributeListGetString(*list, NULL, "printer-name", &rp); 305 papiAttributeListGetString(*list, NULL, "rp", &rp); 306 307 if (rp != NULL) { /* fill in the uri if we have the data */ 308 char buf[BUFSIZ]; 309 310 snprintf(buf, sizeof (buf), "lpd://%s/printers/%s", 311 string, rp); 312 papiAttributeListAddString(list, PAPI_ATTR_APPEND, 313 "printer-uri-supported", strdup(buf)); 314 return; 315 } 316 } 317 318 /* if were are here, we don't have a printer-uri-supported */ 319 } 320 321 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC 322 static void 323 fill_printer_uri(papi_attribute_t ***list) 324 { 325 papi_attribute_t *attribute; 326 char *uri = NULL; 327 328 if ((list == NULL) || (*list == NULL)) 329 return; 330 331 /* do we have a printer-uri */ 332 attribute = papiAttributeListFind(*list, "printer-uri"); 333 if (attribute != NULL) /* we have what we need, return */ 334 return; 335 336 /* 337 * this is sufficient to fool libgnomeprintpapi, but not promote it's 338 * use in the future. 339 */ 340 papiAttributeListAddString(list, PAPI_ATTR_EXCL, "printer-uri", 341 "broken printer-uri semantic"); 342 } 343 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */ 344 345 static void 346 cvt_all_to_member_names(papi_attribute_t ***list) 347 { 348 papi_status_t status; 349 void *iter = NULL; 350 char *string = NULL; 351 352 papiAttributeListGetString(*list, NULL, "member-names", &string); 353 if (string != NULL) /* already have a member-names */ 354 return; 355 356 for (status = papiAttributeListGetString(*list, &iter, "all", &string); 357 status == PAPI_OK; 358 status = papiAttributeListGetString(*list, &iter, NULL, &string)) { 359 char *s_iter = NULL, *value, *tmp = strdup(string); 360 361 for (value = strtok_r(tmp, ", \t", &s_iter); 362 value != NULL; 363 value = strtok_r(NULL, ", \t", &s_iter)) 364 papiAttributeListAddString(list, PAPI_ATTR_APPEND, 365 "member-names", value); 366 free(tmp); 367 } 368 } 369 370 static papi_attribute_t ** 371 _cvt_nss_entry_to_printer(char *entry) 372 { 373 char *key = NULL, 374 *cp, 375 buf[BUFSIZ]; 376 int in_namelist = 1, buf_pos = 0; 377 papi_attribute_t **list = NULL; 378 379 if (entry == NULL) 380 return (NULL); 381 382 memset(buf, 0, sizeof (buf)); 383 for (cp = entry; *cp != '\0'; cp++) { 384 switch (*cp) { 385 case ':': /* end of kvp */ 386 if (in_namelist != 0) { 387 papiAttributeListAddString(&list, 388 PAPI_ATTR_APPEND, "printer-name", buf); 389 in_namelist = 0; 390 } else if (key != NULL) 391 papiAttributeListAddString(&list, 392 PAPI_ATTR_APPEND, key, buf); 393 memset(buf, 0, sizeof (buf)); 394 buf_pos = 0; 395 key = NULL; 396 break; 397 case '=': /* kvp seperator */ 398 if (key == NULL) { 399 key = strdup(buf); 400 memset(buf, 0, sizeof (buf)); 401 buf_pos = 0; 402 } else 403 buf[buf_pos++] = *cp; 404 break; 405 case '|': /* namelist seperator */ 406 if (in_namelist != 0) { 407 papiAttributeListAddString(&list, 408 PAPI_ATTR_APPEND, "printer-name", buf); 409 memset(buf, 0, sizeof (buf)); 410 buf_pos = 0; 411 } else /* add it to the buffer */ 412 buf[buf_pos++] = *cp; 413 break; 414 case '\\': /* escape char */ 415 buf[buf_pos++] = *(++cp); 416 break; 417 default: 418 buf[buf_pos++] = *cp; 419 } 420 421 } 422 423 if (key != NULL) 424 papiAttributeListAddString(&list, PAPI_ATTR_APPEND, key, buf); 425 426 /* resolve any "use" references in the configuration DB */ 427 key = NULL; 428 papiAttributeListGetString(list, NULL, "use", &key); 429 if (key != NULL) { 430 papi_attribute_t **use_attrs = getprinterbyname(key, NULL); 431 432 list_concatenate(&list, use_attrs); 433 } 434 435 fill_printer_uri_supported(&list); 436 cvt_all_to_member_names(&list); /* convert "all" to "member-names" */ 437 438 return (list); 439 } 440 441 #if defined(NSS_SOLARIS) && !defined(NSS_EMULATION) 442 443 #ifndef NSS_DBNAM__PRINTERS /* not in nss_dbdefs.h because it's private */ 444 #define NSS_DBNAM__PRINTERS "_printers" 445 #endif 446 447 static DEFINE_NSS_DB_ROOT(db_root); 448 static DEFINE_NSS_GETENT(context); 449 450 static char *private_ns = NULL; 451 452 static void 453 _nss_initf_printers(p) 454 nss_db_params_t *p; 455 { 456 if (private_ns != NULL) { 457 /* 458 * because we need to support a legacy interface that allows 459 * us to select a specific name service, we need to dummy up 460 * the parameters to use a private nsswitch database and set 461 * the * default_config entry to the name service we are 462 * looking into. 463 */ 464 p->name = NSS_DBNAM__PRINTERS; /* "_printers" */ 465 p->default_config = private_ns; 466 } else { 467 /* regular behaviour */ 468 p->name = NSS_DBNAM_PRINTERS; /* "printers" */ 469 p->default_config = NSS_DEFCONF_PRINTERS; 470 } 471 syslog(LOG_DEBUG, "database: %s, default: %s", 472 (p->name ? p->name : "NULL"), 473 (p->default_config ? p->default_config : "NULL")); 474 } 475 476 /* 477 * Return values: 0 = success, 1 = parse error, 2 = erange ... 478 * The structure pointer passed in is a structure in the caller's space 479 * wherein the field pointers would be set to areas in the buffer if 480 * need be. instring and buffer should be separate areas. 481 */ 482 /* ARGSUSED */ 483 static int 484 str2printer(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 485 { 486 if (lenstr + 1 > buflen) 487 return (NSS_STR_PARSE_ERANGE); 488 489 /* skip entries that begin with '#' */ 490 if (instr[0] == '#') 491 return (NSS_STR_PARSE_PARSE); 492 493 /* 494 * We copy the input string into the output buffer 495 */ 496 (void) memcpy(buffer, instr, lenstr); 497 buffer[lenstr] = '\0'; 498 499 return (NSS_STR_PARSE_SUCCESS); 500 } 501 #endif /* NSS_SOLARIS */ 502 503 int 504 setprinterentry(int stayopen, char *ns) 505 { 506 #ifdef NSS_EMULATION 507 emul_setprinterentry(stayopen); 508 #elif NSS_SOLARIS 509 private_ns = ns; 510 nss_setent(&db_root, _nss_initf_printers, &context); 511 private_ns = NULL; 512 #endif 513 return (0); 514 } 515 516 517 int 518 endprinterentry(int i) 519 { 520 #ifdef NSS_EMULATION 521 emul_endprinterentry(); 522 #elif NSS_SOLARIS 523 nss_endent(&db_root, _nss_initf_printers, &context); 524 nss_delete(&db_root); 525 private_ns = NULL; 526 #endif 527 return (0); 528 } 529 530 /* ARGSUSED2 */ 531 papi_attribute_t ** 532 getprinterentry(char *ns) 533 { 534 papi_attribute_t **result = NULL; 535 536 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS) 537 char buf[10240]; 538 nss_status_t res = NSS_NOTFOUND; 539 540 #ifdef NSS_EMULATION 541 res = emul_getprinterentry_r(buf, sizeof (buf)); 542 #elif NSS_SOLARIS 543 nss_XbyY_args_t arg; 544 545 private_ns = ns; 546 NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer); 547 res = nss_getent(&db_root, _nss_initf_printers, &context, &arg); 548 (void) NSS_XbyY_FINI(&arg); 549 private_ns = NULL; 550 #endif 551 552 if (res != NSS_SUCCESS) 553 buf[0] = '\0'; 554 555 result = _cvt_nss_entry_to_printer(buf); 556 #if defined(__sun) && defined(__SVR4) 557 solaris_lpsched_shortcircuit_hack(&result); 558 #endif 559 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC 560 fill_printer_uri(&result); 561 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */ 562 #endif 563 564 #ifdef DEBUG 565 printf("getprinterentry(%s): 0x%8.8x\n", (ns ? ns : "NULL"), result); 566 if (result != NULL) { 567 char buf[4096]; 568 569 papiAttributeListToString(result, "\n\t", buf, sizeof (buf)); 570 printf("\t%s\n", buf); 571 } 572 #endif /* DEBUG */ 573 574 return (result); 575 } 576 577 578 papi_attribute_t ** 579 getprinterbyname(char *name, char *ns) 580 { 581 papi_attribute_t **result = NULL; 582 583 if (strstr(name, "://") != NULL) { /* shortcut for URI form */ 584 papiAttributeListAddString(&result, PAPI_ATTR_APPEND, 585 "printer-name", name); 586 papiAttributeListAddString(&result, PAPI_ATTR_APPEND, 587 "printer-uri-supported", name); 588 } else if (strchr(name, ':') != NULL) { /* shortcut for POSIX form */ 589 char *uri = bsdaddr_to_uri(result, name); 590 591 papiAttributeListAddString(&result, PAPI_ATTR_APPEND, 592 "printer-name", name); 593 if (uri != NULL) { 594 papiAttributeListAddString(&result, PAPI_ATTR_APPEND, 595 "printer-uri-supported", uri); 596 free(uri); 597 } 598 } else { /* anything else */ 599 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS) 600 char buf[10240]; 601 nss_status_t res = NSS_NOTFOUND; 602 603 #ifdef NSS_EMULATION 604 res = emul_getprinterbyname_r(name, buf, sizeof (buf)); 605 #elif NSS_SOLARIS 606 nss_XbyY_args_t arg; 607 608 private_ns = ns; 609 NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer); 610 arg.key.name = name; 611 res = nss_search(&db_root, _nss_initf_printers, 612 NSS_DBOP_PRINTERS_BYNAME, &arg); 613 (void) NSS_XbyY_FINI(&arg); 614 private_ns = NULL; 615 616 if (res != NSS_SUCCESS) 617 buf[0] = '\0'; 618 #endif 619 620 result = _cvt_nss_entry_to_printer(buf); 621 #endif 622 } 623 #if defined(__sun) && defined(__SVR4) 624 solaris_lpsched_shortcircuit_hack(&result); 625 #endif 626 #ifdef DEBUG 627 printf("getprinterbyname(%s): %s = 0x%8.8x\n", (ns ? ns : "NULL"), 628 name, result); 629 if (result != NULL) { 630 char buf[4096]; 631 632 papiAttributeListToString(result, "\n\t", buf, sizeof (buf)); 633 printf("\t%s\n", buf); 634 } 635 #endif /* DEBUG */ 636 637 return (result); 638 } 639