/* * restore(npop, npush) restore original state of stream * * pops 'npop' modules, then pushes the topmost 'npush' modules from * Oldlist * */ static void restore(int npop, int npush) { int i; if ((i = pop_modules(npop)) != npop) { (void) fprintf(stderr, "%s: WARNING: could not restore state of stream\n", Cmd_namep); return; } if (npush >= Oldlist.sl_nmods) { /* "cannot" happen */ (void) fprintf(stderr, "%s: internal logic error in restore\n", Cmd_namep); (void) fprintf(stderr, "%s: WARNING: could not restore state of stream\n", Cmd_namep); return; } for (i = npush - 1; i >= 0; --i) { if (push_module(Oldlist.sl_modlist[i].l_name) == FAILURE) { (void) fprintf(stderr, "%s: WARNING: could not restore state of stream\n", Cmd_namep); return; } } }
int open_direct(char *ptype, PRINTER *pp) { short bufsz = -1, cps = -1; int open_mode, fd; register unsigned int oldalarm, newalarm = 0; char *device; struct ecpp_transfer_parms ecpp_params; /* for ECPP port checking */ char **modules = NULL; struct flock lck; struct stat buf; register void (*oldsig)() = signal(SIGALRM, sigalrm); /* * Set an alarm to wake us from trying to open the port. * We'll try at least 60 seconds, or more if the printer * has a huge buffer that, in the worst case, would take * a long time to drain. */ tidbit(ptype, "bufsz", &bufsz); tidbit(ptype, "cps", &cps); if (bufsz > 0 && cps > 0) newalarm = (((long)bufsz * 1100) / cps) / 1000; if (newalarm < 60) newalarm = 60; oldalarm = alarm(newalarm); device = pp->device; if (is_printer_uri(device) == 0) { /* * if it's a device uri and the endpoint contains a valid * path, that path should be opened/locked by lpsched for * the backend. If not, the uri isn't associated with a * local device, so use /dev/null. */ device = strstr(device, "://"); if (device != NULL) device = strchr(device + 3, '/'); if ((device == NULL) || (access(device, F_OK) < 0)) device = "/dev/null"; } /* * The following open must be interruptable. * O_APPEND is set in case the ``port'' is a file. * O_RDWR is set in case the interface program wants * to get input from the printer. Don't fail, though, * just because we can't get read access. */ open_mode = O_WRONLY; if (access(device, R_OK) == 0) open_mode = O_RDWR; open_mode |= O_APPEND; SigAlrm = 0; while ((fd = open(device, open_mode, 0)) == -1) { if (errno != EINTR) return (EXEC_EXIT_NPORT); else if (SigAlrm) return (EXEC_EXIT_TMOUT); } alarm(oldalarm); signal(SIGALRM, oldsig); /* * Lock the file in case two "printers" are defined on the * same port. Don't lock /dev/null. */ lck.l_type = F_WRLCK; lck.l_whence = 0; lck.l_start = 0L; lck.l_len = 0L; if (strcmp(device, "/dev/null") && Fcntl(fd, F_SETLKW, &lck) < 0) { execlog("lock error: %s\n", pp->device); return (EXEC_EXIT_NPORT); } /* * We should get the correct channel number (1), but just * in case.... */ if (fd != 1) { dup2(fd, 1); Close(fd); } /* * Handle streams modules: */ if (fstat(1, &buf)) buf.st_mode = 0; /* * for some unknown reason, lpsched appears to pop the streams * modules off the device and push back some "default" ones, * unless a specific set were specified with the printer configuration. * This behaviour causes problems with the ECPP port, so if we have * an ECPP port, and nobody specified a set of modules to use, we * should leave it alone. Normally, we would not bother to play with * the streams modules, but it is possible that someone has come * to rely on this behaviour for other devices. */ if ((pp->modules != NULL) && (pp->modules[0] != NULL) && (strcmp(pp->modules[0], "default") != 0)) modules = pp->modules; if ((modules == NULL) && (ioctl(1, ECPPIOC_GETPARMS, &ecpp_params) < 0)) modules = getlist(DEFMODULES, LP_WS, LP_SEP); /* if "nopush" is supplied, leave the modules alone */ if ((modules != NULL) && (modules[0] != NULL) && (strcasecmp(modules[0], "nopush") == 0)) modules = NULL; /* * If we have a stream and a list of modules to use, then pop the old * modules and push the new ones. */ if ((modules != NULL) && !S_ISFIFO(buf.st_mode) && isastream(1)) { /* * First, pop all current modules off, unless * instructed not to. */ while (ioctl(1, I_POP, 0) == 0) ; /* * Now push either the administrator specified modules * or the standard modules, unless instructed to push * nothing. */ if ((modules[1] == NULL) && (strcasecmp(modules[0], "none") == 0)) return (0); while (*modules) if (push_module(1, device, *modules++) == -1) return (EXEC_EXIT_NPUSH); } return (0); }
int main(int argc, char **argv) { char buf[BUFSIZ]; /* input buffer */ char *file_namep; /* file from -f opt */ char *modnamep; /* mods from -h or -u opt */ char *modp; /* for walking thru modnamep */ FILE *fp; /* file pointer for -f file */ int i; /* loop index and junk var */ int j; /* loop index and junk var */ int euid; /* effective uid */ short error; /* TRUE if usage error */ short fromfile; /* TRUE if -f file */ short is_a_tty; /* TRUE if TCGETA succeeds */ short pop; /* TRUE if -p */ short popall; /* TRUE if -p -a */ short popupto; /* TRUE if -p -u module */ short push; /* TRUE if -h mod1[,mod2 ...] */ struct str_mlist newmods[NMODULES]; /* mod list for new list */ struct stat stats; /* stream stats */ struct str_list newlist; /* modules to be pushed */ struct termio termio; /* save state of tty */ /* * init */ Cmd_namep = argv[0]; error = fromfile = is_a_tty = pop = popall = popupto = push = FALSE; Oldlist.sl_modlist = Oldmods; Oldlist.sl_nmods = NMODULES; newlist.sl_modlist = newmods; newlist.sl_nmods = NMODULES; /* * only owner and root can change stream configuration */ if ((euid = geteuid()) != 0) { if (fstat(0, &stats) < 0) { perror("fstat"); (void) fprintf(stderr, "%s: fstat of stdin failed\n", Cmd_namep); return (ERR_STDIN); } if (euid != stats.st_uid) { (void) fprintf(stderr, "%s: not owner of stdin\n", Cmd_namep); return (ERR_PERM); } } /* * parse args */ if (argc == 1) { (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); return (ERR_USAGE); } while (!error && (i = getopt(argc, argv, OPTLIST)) != -1) { switch (i) { case 'a': /* pop All */ if (fromfile || popupto || push) error = TRUE; else popall = TRUE; break; case 'f': /* read from File */ if (pop || push) error = TRUE; else { fromfile = TRUE; file_namep = optarg; } break; case 'h': /* pusH */ if (fromfile || pop) error = TRUE; else { push = TRUE; modnamep = optarg; } break; case 'p': /* poP */ if (fromfile || push) error = TRUE; else pop = TRUE; break; case 'u': /* pop Upto */ if (fromfile || popall || push) error = TRUE; else { popupto = TRUE; modnamep = optarg; } break; default: (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); return (ERR_USAGE); /*NOTREACHED*/ } } if (error || optind < argc) { (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); return (ERR_USAGE); } if (!pop && (popall || popupto)) { (void) fprintf(stderr, "%s: -p option must be used with -a or -u to pop modules\n", Cmd_namep); (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); return (ERR_USAGE); } /* * Save state so can restore if something goes wrong * (If are only going to push modules, don't need to * save original module list for restore.) */ if (fromfile || pop) { /* * get number of modules on stream * allocate more room if needed */ if ((i = ioctl(STDIN, I_LIST, NULL)) < 0) { perror("I_LIST"); (void) fprintf(stderr, "%s: I_LIST ioctl failed\n", Cmd_namep); return (ERR_STDIN); } if (i > Oldlist.sl_nmods && more_modules(&Oldlist, i) != SUCCESS) return (ERR_MEM); /* * get list of modules on stream */ Oldlist.sl_nmods = i; if (ioctl(STDIN, I_LIST, &Oldlist) < 0) { perror("I_LIST"); (void) fprintf(stderr, "%s: I_LIST ioctl failed\n", Cmd_namep); return (ERR_STDIN); } /* * The following attempts to avoid leaving a * terminal line that does not respond to anything * if the strchg -h or -f options failed due to * specifying invalid module names for pushing */ if (ioctl(STDIN, TCGETA, &termio) >= 0) is_a_tty = TRUE; } /* * push modules on stream */ if (push) { /* * pull mod names out of comma-separated list */ for (i = 0, modp = strtok(modnamep, ","); modp != NULL; ++i, modp = strtok(NULL, ",")) { if (push_module(modp) == FAILURE) { /* pop the 'i' modules we just added */ restore(i, 0); return (ERR_STDIN); } } return (SUCCESS); } /* * read configuration from a file */ if (fromfile) { if ((fp = fopen(file_namep, "r")) == NULL) { perror("fopen"); (void) fprintf(stderr, "%s: could not open file '%s'\n", Cmd_namep, file_namep); return (ERR_OPEN); } /* * read file and construct a new strlist */ i = 0; while (fgets(buf, BUFSIZ, fp) != NULL) { if (buf[0] == '#') continue; /* skip comments */ /* * skip trailing newline, trailing and leading * whitespace */ if ((modp = strtok(buf, " \t\n")) == NULL) continue; /* blank line */ (void) strncpy(newlist.sl_modlist[i].l_name, modp, FMNAMESZ); ++i; if ((modp = strtok(NULL, " \t\n")) != NULL) { /* * bad format * should only be one name per line */ (void) fprintf(stderr, "%s: error on line %d in file %s: " "multiple module names??\n", Cmd_namep, i, file_namep); return (ERR_MODULE); } if (i > newlist.sl_nmods) if (more_modules(&newlist, i) != SUCCESS) return (ERR_MEM); } newlist.sl_nmods = i; /* * If an empty file, exit silently */ if (i == 0) return (SUCCESS); /* * Pop all modules currently on the stream. */ if ((i = pop_modules(Oldlist.sl_nmods - 1)) != (Oldlist.sl_nmods - 1)) { /* put back whatever we've popped */ restore(0, i); return (ERR_STDIN); } /* * Push new modules */ for (i = newlist.sl_nmods - 1; i >= 0; --i) { if (push_module(newlist.sl_modlist[i].l_name) == FAILURE) { /* * pop whatever new modules we've pushed * then push old module list back on */ restore((newlist.sl_nmods - 1 - i), (Oldlist.sl_nmods - 1)); /* * If the stream is a tty line, at least try * to set the state to what it was before. */ if (is_a_tty && ioctl(STDIN, TCSETA, &termio) < 0) { perror("TCSETA"); (void) fprintf(stderr, "%s: WARNING: Could not restore " "the states of the terminal line " "discipline\n", Cmd_namep); } return (ERR_STDIN); } } return (SUCCESS); } /* end if-fromfile */ /* * pop all modules (except driver) */ if (popall) { if (Oldlist.sl_nmods > 1) { if ((i = pop_modules(Oldlist.sl_nmods - 1)) != (Oldlist.sl_nmods - 1)) { restore(0, i); return (ERR_STDIN); } } return (SUCCESS); } /* * pop up to (but not including) a module */ if (popupto) { /* * check that the module is in fact on the stream */ for (i = 0; i < Oldlist.sl_nmods; ++i) if (strncmp(Oldlist.sl_modlist[i].l_name, modnamep, FMNAMESZ) == 0) break; if (i == Oldlist.sl_nmods) { /* no match found */ (void) fprintf(stderr, "%s: %s not found on stream\n", Cmd_namep, modnamep); return (ERR_MODULE); } if ((j = pop_modules(i)) != i) { /* put back whatever we've popped */ restore(0, j); return (ERR_STDIN); } return (SUCCESS); } /* * pop the topmost module */ if (pop) { if (Oldlist.sl_nmods > 1) if (pop_modules(1) != 1) /* no need to restore */ return (ERR_STDIN); return (SUCCESS); } return (SUCCESS); }