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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 #include <stdio.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <locale.h> 34 35 #include "lp.h" 36 #include "access.h" 37 #include "filters.h" 38 #include "msgs.h" 39 40 #define WHO_AM_I I_AM_LPFILTER 41 #include "oam.h" 42 43 #define OPT_LIST "f:F:ixl" 44 45 int add_filter(), 46 reload_filter(), 47 delete_filter(), 48 list_filter(); 49 50 static void alert_spooler(), 51 same_complaints(); 52 53 static char *opt(); 54 55 /* 56 * Unfortunately, the LP requirements show the listing of a filter 57 * to be in a different order than the stored filter table. We can't 58 * change the stored version because it's the same as UNISON uses. 59 * So, we can't reuse the "FL_..." #defines found in "filters.h". 60 * But the following have similar use. 61 */ 62 #define FL_MAX_P FL_MAX 63 # define FL_IGN_P 8 64 # define FL_PTYPS_P 2 65 # define FL_PRTRS_P 3 66 # define FL_ITYPS_P 0 67 # define FL_NAME_P 7 68 # define FL_OTYPS_P 1 69 # define FL_TYPE_P 4 70 # define FL_CMD_P 5 71 # define FL_TMPS_P 6 72 73 #define TABLE 0 74 #define TABLE_I 1 75 76 static struct headings { 77 char *v; 78 short len; 79 } headings[FL_MAX_P] = { 80 81 #define ENTRY(X) X, sizeof(X)-1 82 ENTRY("Input types:"), 83 ENTRY("Output types:"), 84 ENTRY("Printer types:"), 85 ENTRY("Printers:"), 86 ENTRY("Filter type:"), 87 ENTRY("Command:"), 88 ENTRY("Options:"), 89 ENTRY(""), 90 ENTRY("") 91 #undef ENTRY 92 93 }; 94 95 /** 96 ** usage() 97 **/ 98 99 void usage () 100 { 101 (void) printf (gettext( 102 "usage:\n" 103 "\n" 104 " (add or change filter)\n" 105 " lpfilter -f filter-name {-F path-name | -}\n" 106 "\n" 107 " (restore delivered filter)\n" 108 " lpfilter -f filter-name -i\n" 109 "\n" 110 " (list a filter)\n" 111 " lpfilter -f filter-name -l\n" 112 "\n" 113 " (list all filters)\n" 114 " lpfilter -f \"all\" -l\n" 115 "\n" 116 " (delete filter)\n" 117 " lpfilter -f filter-name -x\n")); 118 119 return; 120 } 121 122 /** 123 ** main() 124 **/ 125 126 int main (argc, argv) 127 int argc; 128 char *argv[]; 129 { 130 extern int optind, 131 opterr, 132 optopt, 133 getopt(); 134 135 extern char *optarg; 136 137 int c, 138 (*action)(), 139 (*newaction)(); 140 141 FILE *input; 142 143 char *filter, 144 *p; 145 char stroptsw[] = "-X"; 146 147 148 (void) setlocale (LC_ALL, ""); 149 #if !defined(TEXT_DOMAIN) 150 #define TEXT_DOMAIN "SYS_TEST" 151 #endif 152 (void) textdomain(TEXT_DOMAIN); 153 154 if (!is_user_admin()) { 155 LP_ERRMSG (ERROR, E_LP_NOTADM); 156 exit (1); 157 } 158 159 action = 0; 160 input = 0; 161 filter = 0; 162 163 opterr = 0; 164 165 while ((c = getopt(argc, argv, OPT_LIST)) != -1) switch (c) { 166 167 case 'f': 168 if (filter) 169 LP_ERRMSG1 (WARNING, E_LP_2MANY, 'f'); 170 filter = optarg; 171 if ( 172 STREQU(NAME_ANY, filter) 173 || STREQU(NAME_NONE, filter) 174 ) { 175 LP_ERRMSG (ERROR, E_LP_ANYNONE); 176 exit (1); 177 } else if (!syn_name(filter)) { 178 LP_ERRMSG1 (ERROR, E_LP_NOTNAME, filter); 179 exit (1); 180 } else if (!*filter) 181 filter = NAME_ALL; 182 break; 183 184 case 'F': 185 if (input) 186 LP_ERRMSG1 (WARNING, E_LP_2MANY, 'F'); 187 if (!(input = fopen(optarg, "r"))) { 188 LP_ERRMSG1 (ERROR, E_FL_OPEN, optarg); 189 exit (1); 190 } 191 newaction = add_filter; 192 goto Check; 193 194 case 'i': 195 newaction = reload_filter; 196 goto Check; 197 198 case 'x': 199 newaction = delete_filter; 200 goto Check; 201 202 case 'l': 203 newaction = list_filter; 204 Check: if (action && newaction != action) { 205 LP_ERRMSG2 ( 206 ERROR, 207 E_LP_AMBIG, 208 opt(action), 209 opt(newaction) 210 ); 211 exit (1); 212 } 213 action = newaction; 214 break; 215 216 default: 217 if (optopt == '?') { 218 usage (); 219 exit (0); 220 } 221 stroptsw[1] = optopt; 222 if (strchr(OPT_LIST, optopt)) 223 LP_ERRMSG1 (ERROR, E_LP_OPTARG, stroptsw); 224 else 225 LP_ERRMSG1 (ERROR, E_LP_OPTION, stroptsw); 226 exit (1); 227 228 } 229 230 if (optind < argc && STREQU(argv[optind], "-")) 231 if (action) { 232 LP_ERRMSG2 (ERROR, E_LP_AMBIG, opt(action), "-"); 233 exit (1); 234 } else { 235 action = add_filter; 236 optind++; 237 } 238 239 if (!filter) { 240 LP_ERRMSG (ERROR, E_FL_NOFILT); 241 exit (1); 242 } 243 244 if (!action) { 245 LP_ERRMSG (ERROR, E_FL_NOACT); 246 exit (1); 247 } 248 249 if (optind < argc) 250 LP_ERRMSG1 (WARNING, E_FL_IGNORE, argv[optind]); 251 252 return ((*action)(filter, input)); 253 } 254 255 /** 256 ** add_filter() 257 **/ 258 259 int add_filter (filter, input) 260 char *filter; 261 FILE *input; 262 { 263 register FILTER *pf, 264 *store, 265 *ps; 266 267 register int fld; 268 269 register char *p; 270 271 char buf[3 * BUFSIZ], 272 *file; 273 274 int line, 275 bad_headings, 276 real_fields[FL_MAX], 277 at_least_one, 278 ret; 279 280 FILTER flbuf; 281 282 283 /* 284 * First we read in the input and parse it into a filter, 285 * storing it in the filter buffer "flbuf". Keep track of 286 * which fields have been given, to avoid overwriting unchanged 287 * fields later. 288 */ 289 290 if (!input) 291 input = stdin; 292 293 for (fld = 0; fld < FL_MAX; fld++) 294 real_fields[fld] = 0; 295 flbuf.templates = 0; 296 297 line = bad_headings = 0; 298 while (fgets(buf, sizeof(buf), input) != NULL) { 299 300 buf[strlen(buf) - 1] = 0; 301 302 line++; 303 304 p = buf + strspn(buf, " \t"); 305 if (!*p || *p == '#') 306 continue; 307 308 for (fld = 0; fld < FL_MAX; fld++) 309 if ( 310 headings[fld].v 311 && headings[fld].len 312 && CS_STRNEQU( 313 p, 314 headings[fld].v, 315 headings[fld].len 316 ) 317 ) { 318 real_fields[fld] = 1; 319 p += headings[fld].len + 1; 320 break; 321 } 322 323 if (fld >= FL_MAX) { 324 325 if (bad_headings++ >= 5) { 326 LP_ERRMSG (ERROR, E_FL_GARBAGE); 327 return (1); 328 } 329 LP_ERRMSG1 (WARNING, E_FL_HEADING, line); 330 331 } else switch (fld) { 332 333 case FL_IGN_P: 334 case FL_NAME_P: 335 break; 336 case FL_CMD_P: 337 flbuf.command = strdup(strip(p)); 338 break; 339 case FL_TYPE_P: 340 flbuf.type = s_to_filtertype(strip(p)); 341 break; 342 case FL_PTYPS_P: 343 flbuf.printer_types = getlist(p, LP_WS, LP_SEP); 344 break; 345 case FL_ITYPS_P: 346 flbuf.input_types = getlist(p, LP_WS, LP_SEP); 347 break; 348 case FL_OTYPS_P: 349 flbuf.output_types = getlist(p, LP_WS, LP_SEP); 350 break; 351 case FL_PRTRS_P: 352 flbuf.printers = getlist(p, LP_WS, LP_SEP); 353 break; 354 case FL_TMPS_P: 355 if (flbuf.templates) { 356 char **temp; 357 358 temp = getlist(p, "", LP_SEP); 359 mergelist (&(flbuf.templates), temp); 360 freelist (temp); 361 } else 362 flbuf.templates = getlist(p, "", LP_SEP); 363 break; 364 365 } 366 367 } 368 if (ferror(input)) { 369 LP_ERRMSG (ERROR, E_FL_READ); 370 return (1); 371 } 372 373 /* 374 * We have the input stored, now get the current copy of the 375 * filter(s). If no filter exists, we create it. 376 */ 377 378 if (STREQU(NAME_ALL, filter)) { 379 380 /* 381 * Adding ``all'' means changing all filters to reflect 382 * the information in the input. We'll preload the 383 * filters so that we know how many there are. 384 */ 385 if ( 386 !(file = getfilterfile(FILTERTABLE)) 387 || loadfilters(file) == -1 388 ) { 389 switch (errno) { 390 case ENOENT: 391 LP_ERRMSG (ERROR, E_FL_NOTALL); 392 break; 393 default: 394 same_complaints (FILTERTABLE, TABLE); 395 break; 396 } 397 return (1); 398 } 399 400 store = (FILTER *)malloc((nfilters + 1) * sizeof(FILTER)); 401 if (!store) { 402 LP_ERRMSG (ERROR, E_LP_MALLOC); 403 return (1); 404 } 405 406 for (ps = store; (pf = getfilter(filter)); ) 407 *ps++ = *pf; 408 ps->name = 0; 409 410 switch (errno) { 411 case ENOENT: 412 if (ps - store != nfilters) { 413 LP_ERRMSG1 ( 414 ERROR, 415 E_FL_STRANGE, 416 getfilterfile(FILTERTABLE) 417 ); 418 return (1); 419 } 420 break; 421 default: 422 same_complaints (FILTERTABLE, TABLE); 423 return (1); 424 } 425 426 } else { 427 428 store = (FILTER *)malloc(2 * sizeof(FILTER)); 429 if (!store) { 430 LP_ERRMSG (ERROR, E_LP_MALLOC); 431 return (1); 432 } 433 434 if ((pf = getfilter(filter))) { 435 store[0] = *pf; 436 } else 437 switch (errno) { 438 case ENOENT: 439 /* 440 * We must be adding a new filter, so 441 * set up default values. Check that 442 * we'll have something reasonable to add. 443 */ 444 pf = store; 445 pf->name = strdup(filter); 446 pf->command = 0; 447 pf->type = fl_slow; 448 pf->printer_types = 0; 449 pf->printers = 0; 450 pf->input_types = 0; 451 pf->output_types = 0; 452 pf->templates = 0; 453 if (!flbuf.command) { 454 LP_ERRMSG (ERROR, E_FL_NOCMD); 455 return (1); 456 } 457 break; 458 default: 459 same_complaints (FILTERTABLE, TABLE); 460 return (1); 461 } 462 463 store[1].name = 0; 464 465 } 466 467 at_least_one = ret = 0; 468 for (ps = store; ps->name; ps++) { 469 470 for (fld = 0; fld < FL_MAX; fld++) 471 if (real_fields[fld]) switch(fld) { 472 case FL_IGN_P: 473 case FL_NAME_P: 474 break; 475 case FL_CMD_P: 476 ps->command = flbuf.command; 477 break; 478 case FL_TYPE_P: 479 ps->type = flbuf.type; 480 break; 481 case FL_PTYPS_P: 482 ps->printer_types = flbuf.printer_types; 483 break; 484 case FL_ITYPS_P: 485 ps->input_types = flbuf.input_types; 486 break; 487 case FL_OTYPS_P: 488 ps->output_types = flbuf.output_types; 489 break; 490 case FL_PRTRS_P: 491 ps->printers = flbuf.printers; 492 break; 493 case FL_TMPS_P: 494 ps->templates = flbuf.templates; 495 break; 496 } 497 498 if (putfilter(ps->name, ps) == -1) { 499 if (errno == EBADF) switch (lp_errno) { 500 case LP_ETEMPLATE: 501 LP_ERRMSG (ERROR, E_FL_BADTEMPLATE); 502 break; 503 case LP_EKEYWORD: 504 LP_ERRMSG (ERROR, E_FL_BADKEY); 505 break; 506 case LP_EPATTERN: 507 LP_ERRMSG (ERROR, E_FL_BADPATT); 508 break; 509 case LP_EREGEX: 510 { 511 char * why; 512 513 extern int regerrno; 514 515 516 switch (regerrno) { 517 case 11: 518 why = "range endpoint too large"; 519 break; 520 case 16: 521 why = "bad number"; 522 break; 523 case 25: 524 why = "\"\\digit\" out of range"; 525 break; 526 case 36: 527 why = "illegal or missing delimiter"; 528 break; 529 case 41: 530 why = "no remembered search string"; 531 break; 532 case 42: 533 why = "\\(...\\) imbalance"; 534 break; 535 case 43: 536 why = "too many \\("; 537 break; 538 case 44: 539 why = "more than 2 numbers given in \\{...\\}"; 540 break; 541 case 45: 542 why = "} expected after \\"; 543 break; 544 case 46: 545 why = "first number exceeds second in \\{...\\}"; 546 break; 547 case 49: 548 why = "[...] imbalance"; 549 break; 550 case 50: 551 why = "regular expression overflow"; 552 break; 553 } 554 LP_ERRMSG1 (ERROR, E_FL_BADREGEX, why); 555 break; 556 } 557 case LP_ERESULT: 558 LP_ERRMSG (ERROR, E_FL_BADRESULT); 559 break; 560 case LP_ENOMEM: 561 errno = ENOMEM; 562 same_complaints (FILTERTABLE, TABLE); 563 break; 564 } else 565 same_complaints (FILTERTABLE, TABLE); 566 ret = 1; 567 break; 568 } else 569 at_least_one = 1; 570 571 } 572 573 if (at_least_one) 574 (void)alert_spooler (); 575 576 return (ret); 577 } 578 579 /** 580 ** reload_filter() 581 **/ 582 583 int reload_filter (filter) 584 char *filter; 585 { 586 register FILTER *pf, 587 *store, 588 *ps; 589 590 char *factory_file; 591 592 int ret, 593 at_least_one; 594 595 /* 596 * ``Manually'' load the archived filters, so that a call 597 * to "getfilter()" will read from them instead of the regular 598 * table. 599 */ 600 if ( 601 !(factory_file = getfilterfile(FILTERTABLE_I)) 602 || loadfilters(factory_file) == -1 603 ) { 604 switch (errno) { 605 case ENOENT: 606 LP_ERRMSG (ERROR, E_FL_NOFACTY); 607 break; 608 default: 609 same_complaints (FILTERTABLE_I, TABLE_I); 610 break; 611 } 612 return (1); 613 } 614 615 if (STREQU(NAME_ALL, filter)) { 616 617 store = (FILTER *)malloc((nfilters + 1) * sizeof(FILTER)); 618 if (!store) { 619 LP_ERRMSG (ERROR, E_LP_MALLOC); 620 return (1); 621 } 622 623 for (ps = store; (pf = getfilter(filter)); ) 624 *ps++ = *pf; 625 ps->name = 0; 626 627 switch (errno) { 628 case ENOENT: 629 if (ps - store != nfilters) { 630 LP_ERRMSG1 ( 631 ERROR, 632 E_FL_STRANGE, 633 getfilterfile(FILTERTABLE_I) 634 ); 635 return (1); 636 } 637 break; 638 default: 639 same_complaints (FILTERTABLE_I, TABLE_I); 640 return (1); 641 } 642 643 } else { 644 645 store = (FILTER *)malloc(2 * sizeof(FILTER)); 646 if (!store) { 647 LP_ERRMSG (ERROR, E_LP_MALLOC); 648 return (1); 649 } 650 651 if (!(pf = getfilter(filter))) switch (errno) { 652 case ENOENT: 653 LP_ERRMSG (ERROR, E_FL_FACTYNM); 654 return (1); 655 default: 656 same_complaints (FILTERTABLE_I, TABLE_I); 657 return (1); 658 } 659 660 store[0] = *pf; 661 store[1].name = 0; 662 663 } 664 665 /* 666 * Having stored the archived filter(s) in our own area, clear 667 * the currently loaded table so that the subsequent calls to 668 * "putfilter()" will read in the regular table. 669 */ 670 trash_filters (); 671 672 at_least_one = ret = 0; 673 for (ps = store; ps->name; ps++) 674 if (putfilter(ps->name, ps) == -1) { 675 same_complaints (FILTERTABLE, TABLE); 676 ret = 1; 677 break; 678 } else 679 at_least_one = 1; 680 681 if (at_least_one) 682 (void)alert_spooler (); 683 684 return (ret); 685 } 686 687 /** 688 ** delete_filter() 689 **/ 690 691 int delete_filter (filter) 692 char *filter; 693 { 694 if (delfilter(filter) == -1) switch (errno) { 695 case ENOENT: 696 LP_ERRMSG1 (ERROR, E_FL_UNKFILT, filter); 697 return (1); 698 default: 699 same_complaints (FILTERTABLE, TABLE); 700 return (1); 701 } 702 703 (void)alert_spooler (); 704 705 return (0); 706 } 707 708 /** 709 ** list_filter() 710 **/ 711 712 static void _list_filter(); 713 714 int list_filter (filter) 715 char *filter; 716 { 717 register FILTER *pf; 718 719 char *nl; 720 721 if (STREQU(NAME_ALL, filter)) { 722 723 nl = ""; 724 while ((pf = getfilter(filter))) { 725 printf (gettext("%s(Filter \"%s\")\n"), nl, pf->name); 726 _list_filter (pf); 727 nl = "\n"; 728 } 729 730 switch (errno) { 731 case ENOENT: 732 return (0); 733 default: 734 same_complaints (FILTERTABLE, TABLE); 735 return (1); 736 } 737 738 } else { 739 740 if ((pf = getfilter(filter))) { 741 _list_filter (pf); 742 return (0); 743 } 744 745 switch (errno) { 746 case ENOENT: 747 LP_ERRMSG1 (ERROR, E_FL_UNKFILT, filter); 748 return (1); 749 default: 750 same_complaints (FILTERTABLE, TABLE); 751 return (1); 752 } 753 754 } 755 } 756 757 static void _list_filter (pf) 758 register FILTER *pf; 759 { 760 register char **pp, 761 *sep; 762 763 register int fld; 764 765 char * head; 766 767 768 for (fld = 0; fld < FL_MAX_P; fld++) switch (fld) { 769 case FL_IGN_P: 770 case FL_NAME_P: 771 break; 772 case FL_CMD_P: 773 printf ( 774 "%s %s\n", 775 headings[fld].v, 776 (pf->command? pf->command : "") 777 ); 778 break; 779 case FL_TYPE_P: 780 printf ( 781 "%s %s\n", 782 headings[fld].v, 783 (pf->type == fl_fast? FL_FAST : FL_SLOW) 784 ); 785 break; 786 case FL_PTYPS_P: 787 pp = pf->printer_types; 788 goto Lists; 789 case FL_ITYPS_P: 790 pp = pf->input_types; 791 goto Lists; 792 case FL_OTYPS_P: 793 pp = pf->output_types; 794 goto Lists; 795 case FL_PRTRS_P: 796 pp = pf->printers; 797 Lists: printlist_qsep = 1; 798 printlist_setup ("", "", LP_SEP, ""); 799 printf ("%s ", headings[fld].v); 800 printlist (stdout, pp); 801 printf ("\n"); 802 break; 803 case FL_TMPS_P: 804 head = makestr(headings[fld].v, " ", (char *)0); 805 printlist_qsep = 1; 806 printlist_setup (head, "", "\n", "\n"); 807 printlist (stdout, pf->templates); 808 break; 809 } 810 811 return; 812 } 813 814 /** 815 ** opt() - GENERATE OPTION FROM FUNCTION NAME 816 **/ 817 818 static char *opt (fnc) 819 int (*fnc)(); 820 { 821 if (fnc == add_filter) 822 return ("-F"); 823 else if (fnc == reload_filter) 824 return ("-i"); 825 else if (fnc == list_filter) 826 return ("-l"); 827 else if (fnc == delete_filter) 828 return ("-x"); 829 else 830 return ("-?"); 831 } 832 833 /** 834 ** alert_spooler() - TELL SPOOLER TO LOAD FILTER TABLE 835 **/ 836 837 static void alert_spooler () 838 { 839 char msgbuf[MSGMAX]; 840 841 int mtype; 842 843 short status; 844 845 /* 846 * If the attempt to open a message queue to the 847 * Spooler fails, assume it isn't running and just 848 * return--don't say anything, `cause the user may 849 * know. Any other failure deserves an error message. 850 */ 851 852 if (mopen() == -1) 853 return; 854 855 (void)putmessage (msgbuf, S_LOAD_FILTER_TABLE); 856 857 if (msend(msgbuf) == -1) 858 goto Error; 859 if (mrecv(msgbuf, MSGMAX) == -1) 860 goto Error; 861 862 mtype = getmessage(msgbuf, R_LOAD_FILTER_TABLE, &status); 863 if (mtype != R_LOAD_FILTER_TABLE) { 864 LP_ERRMSG1 (ERROR, E_LP_BADREPLY, mtype); 865 (void)mclose (); 866 exit (1); 867 } 868 869 if (status == MOK) 870 goto NoError; 871 872 Error: LP_ERRMSG (ERROR, E_FL_NOSPLOAD); 873 874 NoError:(void)mclose (); 875 return; 876 877 } 878 879 /** 880 ** same_complaints() - PRINT COMMON ERROR MESSAGES 881 **/ 882 883 static void same_complaints (table, type) 884 char *table; 885 int type; 886 { 887 switch (errno) { 888 case EACCES: 889 if (type == TABLE) 890 LP_ERRMSG1 ( 891 ERROR, 892 E_FL_ACCESS, 893 getfilterfile(table) 894 ); 895 else 896 LP_ERRMSG1 ( 897 ERROR, 898 E_FL_ACCESSI, 899 getfilterfile(table) 900 ); 901 break; 902 case EAGAIN: 903 case EDEADLK: 904 LP_ERRMSG1 (ERROR, E_LP_AGAIN, getfilterfile(table)); 905 break; 906 default: 907 LP_ERRMSG2 ( 908 ERROR, 909 E_FL_UNKNOWN, 910 getfilterfile(table), 911 strerror(errno) 912 ); 913 break; 914 } 915 return; 916 } 917