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