lp.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/systeminfo.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <termios.h>
#include <libintl.h>
#include <locale.h>
#include <pwd.h>
#include <grp.h>
/*
* This program will submit print jobs to a spooler using the BSD
* printing protcol as defined in RFC1179, plus some extension for
* support of additional lp functionality.
*/
extern char *optarg;
extern char *getenv(const char *);
#define SEND_RETRY -1
#define SEND_ABORT -2
static int priority = -1,
copies = 1,
linked = 0,
mail = 0,
delete = 0,
suppress = 1,
banner = 1,
connection_failed = 0;
*notification = NULL,
*internal_type = NULL,
static struct s5_types {
char *name;
char type;
} output_types[] = { /* known LP "-T" types */
/*
* Switched to ASCII, because some BSD systems don't like the 'o' file
* type.
*/
{ "postscript", CF_PRINT_ASCII },
{ "ps", CF_PRINT_ASCII },
{ "simple", CF_PRINT_ASCII },
{ "ascii", CF_PRINT_ASCII },
{ "raw", CF_PRINT_RAW },
{ "dvi", CF_PRINT_DVI },
{ "tex", CF_PRINT_DVI },
{ "raster", CF_PRINT_RAS },
{ "ditroff", CF_PRINT_DROFF },
{ "otroff", CF_PRINT_TROFF },
{ "troff", CF_PRINT_DROFF },
{ "cif", CF_PRINT_CIF },
{ "plot", CF_PRINT_PLOT },
{ "fortran", CF_PRINT_FORT },
{ "pr", CF_PRINT_PR },
};
/*ARGSUSED*/
static void sigbus_handler(int i)
{
exit(-1);
}
/*ARGSUSED*/
static void sigpipe_handler(int i)
{
}
#ifdef OLD_LP
/*
* this will submit the job to a local lpsched using the old interface.
* the argument vector is rebuilt with a new destination, because
* the old name may have been an alias or because it was actually
* lpr(1b) that was called.
*/
static void
{
int argc = 0;
char **argv;
/*
* We allocate the space for ac+5 items, which include all the file
* arguments(ac), generic arguments(OLD_LP, "-d" and "printer") and
* "-s" option of lpr. The extra item is just a cushion.
*/
gettext("not enough memory for argument vector\n"));
exit(1);
}
int i = 0;
while (++i < ac)
i++;
} else { /* convert lpr options */
if (linked == 0)
if (copies > 1) {
char buf[12];
}
if (banner == 0) {
}
title);
}
if (width > 0) {
char buf[16];
}
if (indent > 0) {
char buf[16];
}
if (mail != 0)
char buf[128];
}
if (type != CF_PRINT_ASCII) {
break;
}
}
}
}
#endif
/*
* cheat and look in the LP interface to determine if a local printer is
* rejecting. If so, don't queue the job. If the printer is remote or
* accepting, queue it. This approximates behaviour of previous releases
* The check is being done this way for performance.
*/
static int
{
char *ptr;
found = 1;
rc = 1;
break;
}
}
}
}
/* if we have'nt found the name it could be a class */
if (!found) {
"r+")) != NULL) {
rc = 1;
break;
}
}
}
}
}
return (rc);
}
/*
* Remove special characters before popen (change them into '_').
*/
static void
clean_string(char *ptr)
{
char *cp;
int len;
cp++;
continue;
}
if (len == 1 &&
(wc == L'[')))
*cp = '_';
}
}
static int _notified = 0;
static void
{
if (_notified++ == 0) {
char *tmp;
}
}
/*
* bsd_options() parses the command line using the BSD lpr semantics and sets
* several global variables for use in building the print request.
*/
static void
{
int c;
"P:#:C:J:T:w:i:hplrstdgvcfmn1:2:3:4:")) != EOF)
switch (c) {
case 'P':
break;
case '#':
break;
case 'C':
break;
case 'J':
break;
case 'T':
break;
case 'w':
break;
case 'm':
mail++;
break;
case 'i': /* this may or may not have an arguement */
indent = 8;
optind--;
} else
break;
case 'h':
banner = 0;
break;
case 'r':
delete = 1;
break;
case 's':
linked = 1;
break;
case 'l' :
type = CF_PRINT_RAW;
break;
case 'd' :
type = CF_PRINT_DVI;
break;
case 't' :
break;
case 'g' :
break;
case 'v' :
type = CF_PRINT_RAS;
break;
case 'c' :
type = CF_PRINT_CIF;
break;
case 'f' :
break;
case 'n' :
break;
case 'o' :
type = CF_PRINT_PS;
break;
case 'p' :
type = CF_PRINT_PR;
break;
case '1' :
break;
case '2' :
break;
case '3' :
break;
case '4' :
break;
default:
gettext("Usage: %s [-Pprinter] [-#num] "
"[-Cclass] [-Jjob] [-Ttitle] [-i [indent]] "
"[-1234 font] [-wcols] [-m] [-h] [-s] "
"[-pltndgvcf] files ...\n"),
av[0]);
exit(1);
}
/*
* The pr filter must be specified with the
* title, width, and indent options
*/
"Warning: title option ignored as the pr "
"filter option was not specified\n"));
"Warning: width option ignored as the pr "
"filter option was not specified\n"));
"Warning: indent option ignored as the pr "
"filter option was not specified\n"));
}
/*
* sysv_options() parses the command line using the BSD lpr semantics and sets
* several global variables for use in building the print request.
*/
static void
{
int c;
#ifdef OLD_LP
/*
* limit ourselves to real user's perms before exec'ing
*/
perror("exec local modify");
} else
"job modification not supported on clients\n"));
exit(-1);
}
#endif
linked = 1;
suppress = 0;
switch (c) {
case 'q':
break;
case 'H':
break;
case 'f':
break;
case 'd':
break;
case 'T':
{
int flag = 0;
for (tmp = output_types;
flag++;
}
if (flag == 0)
else
break;
}
case 'S':
break;
case 'o':
{
/*
* -o nobanner will no longer generate a warning or
* Onobanner in the control file. If "nobanner" is
* embedded in an option list, the option list will
* still generate a warning or 'O' message in the
* control file.
*/
s5options = (char **)list_append(
(void**)s5options,
if (strcmp(p, "nobanner") == 0) {
banner = 0;
break;
}
}
break;
case 'y':
{
if (strcmp(p, "catv_filter") == 0)
type = CF_PRINT_RAW;
else
mode = (char **)list_append(
(void **)mode,
(void *)p);
}
break;
case 'P':
break;
case 'i':
"job modification (-i) only supported on server\n"));
break;
case 'c':
linked = 0;
break;
case 'm':
mail++;
break;
case 'w':
mail++;
break;
case 'p':
break;
case 'n':
"-n requires a positive integer argument\n"));
exit(1);
}
break;
case 's':
suppress = 1;
break;
case 't':
break;
case 'r':
/* not supported - raw */
break;
default:
gettext("Usage: %s [-d dest] [-cmwsr] [-n num] "
"[-t title] [-p notification] [-P page-list] "
"[-i job-id] [y modes] [-o options] "
"[-S char-set] [-T input-type] [H handling] "
"[-q priority] files ...\n"),
av[0]);
exit(1);
}
}
/*
* stdin_to_file() reads standard input into a file and returns the file name
* to the caller
*/
static char *
{
int fd,
rc;
char *name,
return (NULL);
return (NULL);
}
return (name);
}
static int
{
int rc = -1;
}
return (rc);
}
/*
* send_job() sends a job to a remote print server.
*/
static int
{
int lockfd,
nd,
tmp,
rc = 0;
return (SEND_RETRY);
}
/* is job complete ? */
connection_failed = 0;
connection_failed = 1;
else
return ((nd == NETWORK_ERROR_UNKNOWN) ||
}
!= 0) {
"send_job failed job %d (%s@%s) check status\n",
gettext("\t\t check queue for (%s@%s)\n"),
return (SEND_RETRY);
}
/*
* attempt to become the job owner: uid, euid, gid, and
* supplementary groups while we try to send the job data.
* The real uid is changed with setreuid() separately from
* changing the effective uid so that we retain the saved
* uid to elevate privilege later. Combining these changes
* would result in a change to the saved uid also and a loss
* of the ability to elevate privilege later.
*/
(void) setuid(0);
}
break; /* there was an error, quit now */
if (p != NULL) {
/*
* lose the supplemental groups and elevate our effective
* other job owners later on.
*/
(void) seteuid(0);
}
if (rc < 0) {
"\t\tdata removed before transfer, job "
"canceled.\n\t\tTry \"lp -c\" or \"lpr\"\n"));
return (SEND_ABORT);
/* probably trying to circumvent file security */
"\t\tunable to read job data.\n"));
return (SEND_ABORT);
} else {
gettext("\t\t check queue for (%s@%s)\n"),
return (SEND_RETRY);
}
}
gettext("\t\t check queue for (%s@%s)\n"),
return (SEND_RETRY);
}
return (0);
}
/*
* xfer_daemon() attempts to start up a daemon for transfering jobs to a remote
* print server. The daemon runs if it can get the master lock, and it
* runs until there are no jobs waiting for transfer.
*/
static void
{
int i,
rc;
closelog();
closefrom(0);
_notified = 1;
(void) dup(1);
(void) setuid(0);
(void) setsid();
if (fork() != 0)
exit(0);
exit(0);
/*
* Bugid: 4133175 printd dies when data is removed or
* permissions are changed. Memory is freed twice.
* Fix: Do not process anything else in the list
* if the return code is SEND_ABORT as the memory
* has already been freed by job_destroy().
*/
tmp++)
if ((connection_failed == 0) &&
(strcmp(p,
(*tmp)->job_printer) == 0))
else
break;
tmp--;
free(s);
free(p);
}
}
/* look for more work to do before we sleep */
(void) sleep(60);
}
}
}
static void
{
}
static char *
build_string(char **list)
{
int size = 0;
size += 16;
}
return (buf);
}
}
buf); \
}
if (value != -1) \
/*
* Main program. if called with "lpr" use the BSD syntax, if called
* with "lp", use the SYSV syntax. If called by any other name,
* attempt to send it to the print server. If the server doesn't
* respond, become a daemon if none is currently running and attempt
* to xfer all waiting jobs.
*/
{
int numFiles = 0,
queueStdin = 0,
exit_code = 0;
char *program,
*user,
hostname[128],
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
else
program++;
/*
* Bugid: 4013980 Application changed fd 1 to a pipe that has
* no reader; we write to stdout and catch a sigpipe and exit.
* Fix: catch signal, complain to syslog, and continue.
*/
if (check_client_spool(NULL) < 0) {
gettext("couldn't validate local spool area (%s)\n"),
return (-1);
}
} else {
xfer_daemon();
return (0);
}
}
}
return (1);
}
printer);
return (1);
}
"%s: requests are not being accepted\n"),
printer);
return (1);
}
#ifdef OLD_LP
/*
* If the server is local, there is lp server configuration, and
* the old lp is still hanging around, use it to submit the job.
*/
{
char cpath[MAXPATHLEN],
}
}
#endif
"Error creating job: check spooling directory: %s",
"Error creating job: check spooling directory: %s\n"),
return (-1);
}
(void) umask(0);
user = get_user_name();
if (banner != 0) {
} else {
}
}
if (mail != 0) {
}
(type == CF_PRINT_TROFF)) {
}
/* RFC1179 compliant don't get this */
} else if (internal_type != NULL)
}
} else {
(priority != -1))
"Warning: %s not configured to handle all lp options:\n"),
printer);
}
queueStdin++;
switch (errno) {
case EISDIR:
"%s: not a regular file\n"),
break;
case ESRCH:
"%s: empty file\n"),
break;
case ENFILE:
"too many files, ignoring %s\n"),
break;
case EOVERFLOW:
"%s: largefile (>= 2GB), ignoring\n"),
break;
default:
}
exit_code = -1;
} else
numFiles++;
} else
queueStdin++;
if (queueStdin != 0) {
char *name;
/* standard input */
gettext("standard input"),
switch (errno) {
case ESRCH:
"standard input empty\n"));
break;
case ENFILE:
"too many files, ignoring standard input\n"));
break;
default:
}
exit_code = -1;
} else
numFiles++;
}
}
if (numFiles == 0)
return (-1);
if (seteuid(0) < 0)
perror("seteuid(0)");
if (suppress == 0)
if (numFiles == 1)
(void) printf(
gettext("request id is %s-%d (%d file)\n"),
else
(void) printf(
gettext("request id is %s-%d (%d files)\n"),
/*
* bgolden 10/2/96
* BUG 1264627
* when executed from xemacs, a sighup will kill
* the child before the job is sent. ignore the signal
*/
switch (fork()) { /* for immediate response */
case -1:
break;
case 0:
break;
default:
return (exit_code);
}
start_daemon(0);
}
else
return (0);
}