| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ |
| /* All Rights Reserved */ |
| |
| |
| /* |
| * Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <sys/types.h> |
| #include <sys/zone.h> |
| #include <stdlib.h> |
| #include <libintl.h> |
| #include <sys/tsol/label_macro.h> |
| #include <bsm/devices.h> |
| #include "lp.h" |
| #include "class.h" |
| #if defined PS_FAULTED |
| #undef PS_FAULTED |
| #endif |
| #include "printers.h" |
| #include "msgs.h" |
| |
| #define WHO_AM_I I_AM_LPADMIN |
| #include "oam.h" |
| |
| #include "lpadmin.h" |
| |
| extern void fromallclasses(); |
| |
| #if !defined(PATH_MAX) |
| # define PATH_MAX 1024 |
| #endif |
| #if PATH_MAX < 1024 |
| # undef PATH_MAX |
| # define PATH_MAX 1024 |
| #endif |
| |
| extern char *label; |
| |
| static void configure_printer(); |
| static char *fullpath(); |
| char *nameit(); |
| static void pack_white(char *ptr); |
| |
| /** |
| ** do_printer() - CREATE OR CHANGE PRINTER |
| **/ |
| |
| void do_printer () |
| { |
| int rc; |
| |
| /* |
| * Set or change the printer configuration. |
| */ |
| if (strlen(modifications)) |
| configure_printer (modifications); |
| |
| /* |
| * Allow/deny forms. |
| */ |
| BEGIN_CRITICAL |
| if (!oldp) |
| if (allow_form_printer(getlist(NAME_NONE, "", ","), p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| |
| if (f_allow || f_deny) { |
| if (f_allow && allow_form_printer(f_allow, p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| |
| if (f_deny && deny_form_printer(f_deny, p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| } |
| END_CRITICAL |
| |
| /* Add/remove types of paper */ |
| |
| BEGIN_CRITICAL |
| if (!oldp) |
| if (add_paper_to_printer(getlist(NAME_NONE, "", ","),p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| |
| |
| if (p_add && add_paper_to_printer(p_add, p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| |
| if (p_remove && remove_paper_from_printer(p_remove, p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| END_CRITICAL |
| |
| /* |
| * Allow/deny users. |
| */ |
| BEGIN_CRITICAL |
| if (!oldp) |
| if (allow_user_printer(getlist(NAME_ALL, "", ","), p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| |
| if (u_allow || u_deny) { |
| if (u_allow && allow_user_printer(u_allow, p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| |
| if (u_deny && deny_user_printer(u_deny, p) == -1) { |
| LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); |
| done(1); |
| } |
| } |
| END_CRITICAL |
| |
| /* |
| * Tell the Spooler about the printer |
| */ |
| send_message(S_LOAD_PRINTER, p, "", ""); |
| rc = output(R_LOAD_PRINTER); |
| |
| switch (rc) { |
| case MOK: |
| break; |
| |
| case MNODEST: |
| case MERRDEST: |
| LP_ERRMSG (ERROR, E_ADM_ERRDEST); |
| done (1); |
| /*NOTREACHED*/ |
| |
| case MNOSPACE: |
| LP_ERRMSG (WARNING, E_ADM_NOPSPACE); |
| break; |
| |
| case MNOPERM: /* taken care of up front */ |
| default: |
| LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); |
| done (1); |
| /*NOTREACHED*/ |
| } |
| |
| /* |
| * Now that the Spooler knows about the printer, |
| * we can do the balance of the changes. |
| */ |
| |
| /* |
| * Mount or unmount form, print-wheel. |
| */ |
| if (M) |
| do_mount(p, (f? f : (char *)0), (S? *S : (char *)0)); |
| else if (t) do_max_trays(p); |
| |
| /* |
| * Display the alert type. |
| */ |
| if (A && STREQU(A, NAME_LIST)) { |
| if (label) |
| (void) printf(gettext("Printer %s: "), label); |
| printalert (stdout, &(oldp->fault_alert), 1); |
| } |
| |
| /* |
| * -A quiet. |
| */ |
| if (A && STREQU(A, NAME_QUIET)) { |
| |
| send_message(S_QUIET_ALERT, p, (char *)QA_PRINTER, ""); |
| rc = output(R_QUIET_ALERT); |
| |
| switch(rc) { |
| case MOK: |
| break; |
| |
| case MNODEST: /* not quite, but not a lie either */ |
| case MERRDEST: |
| LP_ERRMSG1 (WARNING, E_LP_NOQUIET, p); |
| break; |
| |
| case MNOPERM: /* taken care of up front */ |
| default: |
| LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); |
| done (1); |
| /*NOTREACHED*/ |
| } |
| } |
| |
| /* |
| * Add printer p to class c |
| */ |
| if (c) { |
| CLASS *pc, |
| clsbuf; |
| |
| if (STREQU(c, NAME_ANY)) |
| c = NAME_ALL; |
| |
| Loop: if (!(pc = getclass(c))) { |
| if (STREQU(c, NAME_ALL)) |
| goto Done; |
| |
| if (errno != ENOENT) { |
| LP_ERRMSG2 ( |
| ERROR, |
| E_LP_GETCLASS, |
| c, |
| PERROR |
| ); |
| done (1); |
| } |
| |
| /* |
| * Create the class |
| */ |
| clsbuf.name = strdup(c); |
| clsbuf.members = 0; |
| if (addlist(&clsbuf.members, p) == -1) { |
| LP_ERRMSG (ERROR, E_LP_MALLOC); |
| done (1); |
| } |
| pc = &clsbuf; |
| |
| } else if (searchlist(p, pc->members)) |
| LP_ERRMSG2 (WARNING, E_ADM_INCLASS, p, pc->name); |
| |
| else if (addlist(&pc->members, p) == -1) { |
| LP_ERRMSG (ERROR, E_LP_MALLOC); |
| done (1); |
| } |
| |
| BEGIN_CRITICAL |
| if (putclass(pc->name, pc) == -1) { |
| LP_ERRMSG2 ( |
| ERROR, |
| E_LP_PUTCLASS, |
| pc->name, |
| PERROR |
| ); |
| done(1); |
| } |
| END_CRITICAL |
| |
| send_message (S_LOAD_CLASS, pc->name); |
| rc = output(R_LOAD_CLASS); |
| |
| switch(rc) { |
| case MOK: |
| break; |
| |
| case MNODEST: |
| case MERRDEST: |
| LP_ERRMSG (ERROR, E_ADM_ERRDEST); |
| done (1); |
| /*NOTREACHED*/ |
| |
| case MNOSPACE: |
| LP_ERRMSG (WARNING, E_ADM_NOCSPACE); |
| break; |
| |
| case MNOPERM: /* taken care of up front */ |
| default: |
| LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); |
| done (1); |
| /*NOTREACHED*/ |
| } |
| |
| if (STREQU(c, NAME_ALL)) |
| goto Loop; |
| } |
| Done: |
| /* |
| * Remove printer p from class r |
| */ |
| if (r) { |
| if (STREQU(r, NAME_ALL) || STREQU(r, NAME_ANY)) |
| fromallclasses(p); |
| else |
| fromclass(p, r); |
| } |
| |
| return; |
| } |
| |
| /** |
| ** configure_printer() - SET OR CHANGE CONFIGURATION OF PRINTER |
| **/ |
| |
| static void configure_printer (list) |
| char *list; |
| { |
| register PRINTER *prbufp; |
| |
| PRINTER printer_struct; |
| |
| char type; |
| char * infile_opts = NULL; |
| |
| |
| if (oldp) { |
| |
| prbufp = oldp; |
| |
| if (!T) |
| T = prbufp->printer_types; |
| |
| if (!i && !e && !m) |
| /* |
| * Don't copy the original interface program |
| * again, but do keep the name of the original. |
| */ |
| ignprinter = BAD_INTERFACE; |
| else |
| ignprinter = 0; |
| |
| /* |
| * If we are making this a remote printer, |
| * make sure that local-only attributes are |
| * cleared. |
| */ |
| if (s) { |
| prbufp->banner = 0; |
| prbufp->cpi.val = 0; |
| prbufp->cpi.sc = 0; |
| prbufp->device = 0; |
| prbufp->dial_info = 0; |
| prbufp->fault_rec = 0; |
| prbufp->interface = 0; |
| prbufp->lpi.val = 0; |
| prbufp->lpi.sc = 0; |
| prbufp->plen.val = 0; |
| prbufp->plen.sc = 0; |
| prbufp->login = 0; |
| prbufp->speed = 0; |
| prbufp->stty = 0; |
| prbufp->pwid.val = 0; |
| prbufp->pwid.sc = 0; |
| prbufp->fault_alert.shcmd = strdup(NAME_NONE); |
| prbufp->fault_alert.Q = 0; |
| prbufp->fault_alert.W = 0; |
| #if defined(CAN_DO_MODULES) |
| prbufp->modules = 0; |
| #endif |
| |
| /* |
| * If we are making this a local printer, make |
| * sure that some local-only attributes are set. |
| * (If the user has specified these as well, his/her |
| * values will overwrite what we set here.) |
| */ |
| } else if (oldp->remote) { |
| prbufp->banner = BAN_ALWAYS; |
| prbufp->interface = makepath(Lp_Model, STANDARD, (char *)0); |
| prbufp->fault_alert.shcmd = nameit(NAME_MAIL); |
| |
| /* |
| * Being here means "!s && oldp->remote" is true, |
| * i.e. this printer never had an interface pgm |
| * before. Thus we can safely clear the following. |
| * This is needed to let "putprinter()" copy the |
| * (default) interface program. |
| */ |
| ignprinter = 0; |
| } |
| |
| } else { |
| /* |
| * The following takes care of the lion's share |
| * of the initialization of a new printer structure. |
| * However, special initialization (e.g. non-zero, |
| * or substructure members) needs to be considered |
| * for EACH NEW MEMBER added to the structure. |
| */ |
| (void)memset (&printer_struct, 0, sizeof(printer_struct)); |
| |
| prbufp = &printer_struct; |
| prbufp->banner = BAN_ALWAYS; |
| prbufp->cpi.val = 0; |
| prbufp->cpi.sc = 0; |
| if (!s) |
| prbufp->interface = makepath(Lp_Model, m, (char *)0); |
| prbufp->lpi.val = 0; |
| prbufp->lpi.sc = 0; |
| prbufp->plen.val = 0; |
| prbufp->plen.sc = 0; |
| prbufp->pwid.val = 0; |
| prbufp->pwid.sc = 0; |
| if (!s && !A) |
| prbufp->fault_alert.shcmd = nameit(NAME_MAIL); |
| prbufp->fault_alert.Q = 0; |
| prbufp->fault_alert.W = 0; |
| prbufp->options = NULL; |
| } |
| |
| while ((type = *list++) != '\0') switch(type) { |
| |
| case 'A': |
| if (!s) { |
| if (STREQU(A, NAME_MAIL) || STREQU(A, NAME_WRITE)) |
| prbufp->fault_alert.shcmd = nameit(A); |
| else if (!STREQU(A, NAME_QUIET)) |
| prbufp->fault_alert.shcmd = A; |
| } |
| break; |
| |
| case 'b': |
| if (!s) |
| prbufp->banner = banner; |
| break; |
| |
| case 'c': |
| if (!s) |
| prbufp->cpi = cpi_sdn; |
| break; |
| |
| case 'D': |
| prbufp->description = D; |
| break; |
| |
| case 'e': |
| if (!s) { |
| prbufp->interface = makepath( |
| Lp_A_Interfaces, |
| e, |
| (char *)0 |
| ); |
| } |
| break; |
| |
| case 'F': |
| if (!s) |
| prbufp->fault_rec = F; |
| break; |
| |
| #if defined(CAN_DO_MODULES) |
| case 'H': |
| if (!s) |
| prbufp->modules = H; |
| break; |
| #endif |
| |
| case 'h': |
| if (!s) |
| prbufp->login = 0; |
| break; |
| |
| case 'i': |
| if (!s) |
| prbufp->interface = fullpath(i); |
| break; |
| |
| case 'I': |
| prbufp->input_types = I; |
| break; |
| |
| case 'l': |
| if (!s) |
| prbufp->login = 1; |
| break; |
| |
| case 'L': |
| if (!s) |
| prbufp->plen = length_sdn; |
| break; |
| |
| case 'm': |
| if (!s) |
| prbufp->interface = makepath(Lp_Model, m, (char *)0); |
| break; |
| |
| case 'M': |
| if (!s) |
| prbufp->lpi = lpi_sdn; |
| break; |
| |
| #ifdef LP_USE_PAPI_ATTR |
| case 'n': |
| { |
| if (n_opt != NULL) |
| { |
| if (*n_opt == '/') |
| { |
| prbufp->ppd = fullpath(n_opt); |
| } |
| else |
| { |
| prbufp->ppd = |
| makepath(Lp_Model, "ppd", n_opt, (char *)0); |
| } |
| ppdopt = 1; |
| } |
| break; |
| } |
| #endif |
| |
| case 'o': |
| /* |
| * The "undefined" key-value -o options |
| * |
| * Options requires special handling. It is a |
| * list whose members are to be handled |
| * individually. |
| * |
| * Need to: set new options, keep old options if not |
| * redefined, remove old options if defined as "key=". |
| * |
| * |
| * "p" is a global containing the printer name |
| */ |
| |
| if (!s) { |
| |
| if ((infile_opts = getpentry(p, PR_OPTIONS)) == NULL) |
| prbufp->options = o_options; |
| else { |
| prbufp->options = |
| pick_opts(infile_opts, o_options); |
| } |
| } |
| break; |
| |
| case 'R': |
| if (s) { |
| prbufp->remote = s; |
| prbufp->dial_info = 0; |
| prbufp->device = 0; |
| } else |
| prbufp->remote = 0; |
| break; |
| |
| case 's': |
| if (!s) { |
| /* |
| * lpadmin always defers to stty |
| */ |
| prbufp->speed = 0; |
| prbufp->stty = stty_opt; |
| } |
| break; |
| |
| case 'S': |
| if (!M) |
| if (STREQU(*S, NAME_NONE)) |
| prbufp->char_sets = 0; |
| else |
| prbufp->char_sets = S; |
| break; |
| |
| case 'T': |
| prbufp->printer_types = T; |
| break; |
| |
| case 'U': |
| if (!s) { |
| prbufp->dial_info = U; |
| prbufp->device = 0; |
| prbufp->remote = 0; |
| } |
| break; |
| |
| case 'v': |
| if (!s) { |
| prbufp->device = v; |
| prbufp->dial_info = 0; |
| prbufp->remote = 0; |
| } |
| break; |
| |
| case 'w': |
| if (!s) |
| prbufp->pwid = width_sdn; |
| break; |
| |
| case 'W': |
| if (!s) |
| prbufp->fault_alert.W = W; |
| break; |
| |
| } |
| |
| |
| BEGIN_CRITICAL |
| if (putprinter(p, prbufp) == -1) { |
| if ( |
| errno == EINVAL |
| && (badprinter & BAD_INTERFACE) |
| ) |
| LP_ERRMSG1 ( |
| ERROR, |
| E_ADM_BADINTF, |
| prbufp->interface |
| ); |
| else |
| LP_ERRMSG2 ( |
| ERROR, |
| E_LP_PUTPRINTER, |
| p, |
| PERROR |
| ); |
| done(1); |
| } |
| |
| if ((getzoneid() == GLOBAL_ZONEID) && system_labeled && |
| (prbufp->device != NULL)) |
| update_dev_dbs(p, prbufp->device, "ADD"); |
| |
| END_CRITICAL |
| |
| return; |
| } |
| |
| /** |
| ** fullpath() |
| **/ |
| |
| static char *fullpath (str) |
| char *str; |
| { |
| register char *cur_dir, |
| *path; |
| |
| |
| while (*str && *str == ' ') |
| str++; |
| if (*str == '/') |
| return (str); |
| |
| if (!(cur_dir = malloc(PATH_MAX + 1))) |
| return (str); |
| |
| getcwd (cur_dir, PATH_MAX); |
| path = makepath(cur_dir, str, (char *)0); |
| |
| /* |
| * Here we could be nice and strip out /./ and /../ |
| * stuff, but it isn't necessary. |
| */ |
| |
| return (path); |
| } |
| |
| /** |
| ** nameit() - ADD USER NAME TO COMMAND |
| **/ |
| |
| char *nameit (cmd) |
| char *cmd; |
| { |
| register char *nm = getname(), |
| *copy = malloc( |
| (unsigned) (strlen(cmd) + 1 + |
| strlen(nm) + 1) |
| ); |
| |
| (void) strcpy (copy, cmd); |
| (void) strcat (copy, " "); |
| (void) strcat (copy, nm); |
| return (copy); |
| } |
| |
| /* |
| * update_dev_dbs - ADD/REMOVE ENTRIES FOR THE PRINTER IN DEVICE |
| * ALLOCATION FILES |
| * |
| * We intentionally ignore errors, since we don't want the printer |
| * installation to be viewed as failing just because we didn't add |
| * the device_allocate entry. |
| * |
| * Input: |
| * prtname - printer name |
| * devname - device associated w/ this printer |
| * func - [ADD|REMOVE] entries in /etc/security/device_allocate |
| * and /etc/security/device_maps |
| * |
| * Return: |
| * Always 'quiet' return. Failures are ignored. |
| */ |
| void |
| update_dev_dbs(char *prtname, char *devname, char *func) |
| { |
| int fd, status; |
| pid_t pid; |
| |
| pid = fork(); |
| switch (pid) { |
| case -1: |
| /* fork failed, just return quietly */ |
| return; |
| case 0: |
| /* child */ |
| /* redirect to /dev/null */ |
| (void) close(1); |
| (void) close(2); |
| fd = open("/dev/null", O_WRONLY); |
| fd = dup(fd); |
| |
| if (strcmp(func, "ADD") == 0) { |
| execl("/usr/sbin/add_allocatable", "add_allocatable", |
| "-n", prtname, "-t", "lp", "-l", devname, |
| "-o", "minlabel=admin_low:maxlabel=admin_high", |
| "-a", "*", "-c", "/bin/true", NULL); |
| } else { |
| if (strcmp(func, "REMOVE") == 0) { |
| execl("/usr/sbin/remove_allocatable", |
| "remove_allocatable", "-n", prtname, NULL); |
| } |
| } |
| _exit(1); |
| /* NOT REACHED */ |
| default: |
| waitpid(pid, &status, 0); |
| return; |
| } |
| } |
| |
| /* |
| * pack_white(ptr) trims off multiple occurances of white space from a NULL |
| * terminated string pointed to by "ptr". |
| */ |
| static void |
| pack_white(char *ptr) |
| { |
| char *tptr; |
| char *mptr; |
| int cnt; |
| |
| if (ptr == NULL) |
| return; |
| cnt = strlen(ptr); |
| if (cnt == 0) |
| return; |
| mptr = (char *)calloc((unsigned)cnt+1, sizeof (char)); |
| if (mptr == NULL) |
| return; |
| tptr = strtok(ptr, " \t"); |
| while (tptr != NULL) { |
| (void) strcat(mptr, tptr); |
| (void) strcat(mptr, " "); |
| tptr = strtok(NULL, " \t"); |
| } |
| cnt = strlen(mptr); |
| (void) strcpy(ptr, mptr); |
| free(mptr); |
| } |