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
* 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 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 <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/systeminfo.h>
#include <sys/param.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>
#include <print/ns.h>
#include <print/network.h>
#include <print/misc.h>
#include <print/list.h>
#include <print/job.h>
/*
* lpr/lp
* 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 int optind, opterr, optopt;
extern char *getenv(const char *);
#define SEND_RETRY -1
#define SEND_ABORT -2
static int priority = -1,
copies = 1,
width = -1, /* pr width */
indent = -1, /* pr indent */
linked = 0,
mail = 0,
delete = 0,
suppress = 1,
banner = 1,
connection_failed = 0;
static char *printer = NULL,
*form = NULL,
*charset = NULL,
*title = NULL, /* pr title */
*class = NULL,
*jobName = NULL,
*notification = NULL,
*handling = NULL,
*pages = NULL,
**mode = NULL,
**s5options = NULL,
*s5type = NULL,
*internal_type = NULL,
*fontR = NULL,
*fontI = NULL,
*fontB = NULL,
*fontS = NULL,
type = CF_PRINT_ASCII;
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 },
NULL
};
/*ARGSUSED*/
static void sigbus_handler(int i)
{
(void) fprintf(stderr,
gettext("No space in /var/spool/print to store job"));
exit(-1);
}
/*ARGSUSED*/
static void sigpipe_handler(int i)
{
syslog(LOG_ERR, "Warning: Received SIGPIPE; continuing");
(void) signal(SIGPIPE, sigpipe_handler);
}
#define OLD_LP "/usr/lib/lp/local/lp" /* for local lpsched printers */
#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
submit_local_lp(char *program, int ac, char *av[])
{
uid_t ruid = getuid();
struct passwd *pw;
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.
*/
if ((argv = (char **)calloc(ac + 5, sizeof (char *))) == NULL) {
(void) fprintf(stderr,
gettext("not enough memory for argument vector\n"));
exit(1);
}
argv[argc++] = OLD_LP;
argv[argc++] = "-d";
argv[argc++] = printer;
if (strcmp(program, "lp") == 0) {
int i = 0;
while (++i < ac)
if (strncmp(av[i], "-d", 2) != 0) {
argv[argc++] = av[i];
} else if (strlen(av[i]) == 2)
i++;
} else { /* convert lpr options */
argv[argc++] = "-s"; /* supress id message */
if (linked == 0)
argv[argc++] = "-c";
if (copies > 1) {
char buf[12];
(void) sprintf(buf, "%d", copies);
argv[argc++] = "-n";
argv[argc++] = strdup(buf);
}
if (banner == 0) {
argv[argc++] = "-o";
argv[argc++] = "nobanner";
}
if (title != NULL) {
char buf[BUFSIZ];
(void) snprintf(buf, sizeof (buf), "prtitle='%s'",
title);
argv[argc++] = "-y";
argv[argc++] = strdup(buf);
}
if (width > 0) {
char buf[16];
(void) sprintf(buf, "prwidth=%d", width);
argv[argc++] = "-y";
argv[argc++] = strdup(buf);
}
if (indent > 0) {
char buf[16];
(void) sprintf(buf, "indent=%d", indent);
argv[argc++] = "-y";
argv[argc++] = strdup(buf);
}
if (mail != 0)
argv[argc++] = "-m";
if ((jobName != NULL) || (class != NULL)) {
char buf[128];
snprintf(buf, sizeof (buf), "%s%s%s",
(jobName ? jobName : ""),
(jobName && class ? "\\n#####\\n#####\\t\\t "
: ""), (class ? class : ""));
argv[argc++] = "-t";
argv[argc++] = strdup(buf);
}
if (type != CF_PRINT_ASCII) {
struct s5_types *tmp;
for (tmp = output_types; tmp->name != NULL; tmp++)
if (tmp->type == type) {
argv[argc++] = "-T";
argv[argc++] = tmp->name;
break;
}
}
while (optind < ac)
argv[argc++] = av[optind++];
}
ruid = getuid();
if ((pw = getpwuid(ruid)) != NULL)
(void) initgroups(pw->pw_name, pw->pw_gid);
(void) setuid(ruid);
argv[argc++] = NULL;
(void) execv(OLD_LP, argv);
}
#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
rejecting(char *printer)
{
int rc = 0, found = 0;
FILE *fp;
if ((fp = fopen("/usr/spool/lp/system/pstatus", "r+")) != NULL) {
char buf[BUFSIZ];
while (fgets(buf, sizeof (buf), fp) != NULL) {
buf[strlen(buf)-1] = NULL;
if (strcmp(buf, printer) == 0) {
char *ptr;
found = 1;
(void) fgets(buf, sizeof (buf), fp);
buf[strlen(buf)-1] = NULL;
ptr = strrchr(buf, ' ');
if (ptr && (strcmp(++ptr, "rejecting") == 0)) {
rc = 1;
break;
}
}
}
}
(void) fclose(fp);
/* if we have'nt found the name it could be a class */
if (!found) {
if ((fp = fopen("/usr/spool/lp/system/cstatus",
"r+")) != NULL) {
char buf2[BUFSIZ];
while (fgets(buf2, sizeof (buf2), fp) != NULL) {
buf2[strlen(buf2)-1] = NULL;
if (strcmp(buf2, printer) == 0) {
fgets(buf2, sizeof (buf2), fp);
buf2[strlen(buf2)-1] = NULL;
if (strcmp(buf2, "rejecting") == 0) {
rc = 1;
break;
}
}
}
}
}
(void) fclose(fp);
return (rc);
}
/*
* Remove special characters before popen (change them into '_').
*/
static void
clean_string(char *ptr)
{
char *cp;
wchar_t wc;
int len;
for (cp = ptr; *cp != NULL; ) {
if ((len = mbtowc(&wc, cp, MB_CUR_MAX)) == -1) {
cp++;
continue;
}
if (len == 1 &&
((wc == L'`') || (wc == L'&') || (wc == L';') ||
(wc == L'|') || (wc == L'>') || (wc == L'^') ||
(wc == L'$') || (wc == L'(') || (wc == L')') ||
(wc == L'<') || (wc == L'*') || (wc == L'?') ||
(wc == L'[')))
*cp = '_';
cp += len;
}
}
static int _notified = 0;
static void
error_notify(char *user, int id, char *msg, ...)
{
if (_notified++ == 0) {
char *tmp;
char cmd[BUFSIZ];
FILE *fp;
va_list ap;
va_start(ap, msg);
tmp = strdup(user);
clean_string(tmp);
(void) snprintf(cmd, sizeof (cmd),
"/bin/write %s >/dev/null 2>&1", tmp);
free(tmp);
fp = popen(cmd, "w+");
(void) fprintf(fp,
gettext("\n\tError transfering print job %d\n"), id);
(void) vfprintf(fp, msg, ap);
(void) pclose(fp);
va_end(ap);
}
}
/*
* 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
bsd_options(int ac, char *av[])
{
int c;
while ((c = getopt(ac, av,
"P:#:C:J:T:w:i:hplrstdgvcfmn1:2:3:4:")) != EOF)
switch (c) {
case 'P':
printer = optarg;
break;
case '#':
copies = atoi(optarg);
break;
case 'C':
class = optarg;
break;
case 'J':
jobName = optarg;
break;
case 'T':
title = optarg;
break;
case 'w':
width = atoi(optarg);
break;
case 'm':
mail++;
break;
case 'i': /* this may or may not have an arguement */
if (isdigit(optarg[0]) == 0) {
indent = 8;
optind--;
} else
indent = atoi(optarg);
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' :
type = CF_PRINT_TROFF;
break;
case 'g' :
type = CF_PRINT_PLOT;
break;
case 'v' :
type = CF_PRINT_RAS;
break;
case 'c' :
type = CF_PRINT_CIF;
break;
case 'f' :
type = CF_PRINT_FORT;
break;
case 'n' :
type = CF_PRINT_DROFF;
break;
case 'o' :
type = CF_PRINT_PS;
break;
case 'p' :
type = CF_PRINT_PR;
break;
case '1' :
fontR = optarg;
break;
case '2' :
fontI = optarg;
break;
case '3' :
fontB = optarg;
break;
case '4' :
fontS = optarg;
break;
default:
(void) fprintf(stderr,
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
*/
if ((title != NULL) && (type != CF_PRINT_PR))
(void) fprintf(stderr, gettext(
"Warning: title option ignored as the pr "
"filter option was not specified\n"));
if ((width > 0) && (type != CF_PRINT_PR))
(void) fprintf(stderr, gettext(
"Warning: width option ignored as the pr "
"filter option was not specified\n"));
if ((indent > 0) && (type != CF_PRINT_PR))
(void) fprintf(stderr, gettext(
"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
sysv_options(int ac, char *av[])
{
int c;
#ifdef OLD_LP
if ((ac > 2) && (strcmp(av[1], "-i") == 0)) {
if (access(OLD_LP, F_OK) == 0) {
/*
* limit ourselves to real user's perms before exec'ing
*/
(void) setuid(getuid());
(void) execv(OLD_LP, av);
perror("exec local modify");
} else
(void) printf(gettext(
"job modification not supported on clients\n"));
exit(-1);
}
#endif
linked = 1;
suppress = 0;
while ((c = getopt(ac, av, "H:P:S:T:d:f:i:o:q:t:y:cmwn:prs")) != EOF)
switch (c) {
case 'q':
priority = atoi(optarg);
break;
case 'H':
handling = optarg;
break;
case 'f':
form = optarg;
break;
case 'd':
printer = optarg;
break;
case 'T':
{
struct s5_types *tmp;
int flag = 0;
for (tmp = output_types;
((flag == 0) && (tmp->name != NULL)); tmp++)
if (strcasecmp(tmp->name, optarg) == 0) {
type = tmp->type;
flag++;
}
if (flag == 0)
s5type = optarg;
else
internal_type = optarg;
break;
}
case 'S':
charset = optarg;
break;
case 'o':
{
char *p, *q = strdup(optarg);
/*
* -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.
*/
if (strcmp("nobanner", optarg) != 0)
s5options = (char **)list_append(
(void**)s5options,
(void *)strdup(optarg));
for (p = strtok(q, "\t ,"); p != NULL;
p = strtok(NULL, "\t ,"))
if (strcmp(p, "nobanner") == 0) {
banner = 0;
break;
}
}
break;
case 'y':
{
char *p, *q = strdup(optarg);
for (p = strtok(q, "\t ,"); p != NULL;
p = strtok(NULL, "\t ,"))
if (strcmp(p, "catv_filter") == 0)
type = CF_PRINT_RAW;
else
mode = (char **)list_append(
(void **)mode,
(void *)p);
}
break;
case 'P':
pages = optarg;
break;
case 'i':
(void) printf(gettext(
"job modification (-i) only supported on server\n"));
break;
case 'c':
linked = 0;
break;
case 'm':
mail++;
break;
case 'w':
mail++;
break;
case 'p':
notification = optarg;
break;
case 'n':
if ((optarg == 0) || (*optarg == '-')) {
(void) fprintf(stderr, gettext(
"-n requires a positive integer argument\n"));
exit(1);
}
copies = atoi(optarg);
break;
case 's':
suppress = 1;
break;
case 't':
jobName = optarg;
break;
case 'r':
/* not supported - raw */
break;
default:
(void) fprintf(stderr,
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 *
stdin_to_file()
{
int fd,
rc;
char *name,
buf[BUFSIZ];
(void) putenv("TMPDIR="); /* stop user moving the temp file */
snprintf(buf, sizeof (buf), "/tmp/stdinXXXXXX");
if ((fd = mkstemp(buf)) < 0)
return (NULL);
fchmod(fd, 0640);
if ((name = strdup(buf)) == NULL) {
close(fd);
return (NULL);
}
syslog(LOG_DEBUG, "stdin_to_file: %s", name);
while ((rc = read(0, buf, sizeof (buf))) > 0)
(void) write(fd, buf, rc);
(void) close(fd);
return (name);
}
static int
sendfile(jobfile_t *file, int nd, int type)
{
int rc = -1;
syslog(LOG_DEBUG, "sendfile(%s, %d, %d)",
((file != NULL) ? file->jf_spl_path : "NULL"), nd, type);
if (file && file->jf_spl_path) {
rc = net_send_file(nd, file->jf_spl_path, file->jf_data,
file->jf_size, type);
}
return (rc);
}
/*
* send_job() sends a job to a remote print server.
*/
static int
send_job(job_t *job)
{
int lockfd,
lock_size,
nd,
tmp,
rc = 0;
struct passwd *p = NULL;
char buf[BUFSIZ];
syslog(LOG_DEBUG, "send_job(%s, %s, %d): called", job->job_printer,
job->job_server, job->job_id);
if ((lockfd = get_lock(job->job_cf->jf_src_path, 0)) < 0) {
(void) close(lockfd);
return (SEND_RETRY);
}
/* is job complete ? */
lock_size = file_size(job->job_cf->jf_src_path);
(void) sprintf(buf, "%ld\n", getpid()); /* add pid to lock file */
(void) lseek(lockfd, 0, SEEK_END);
(void) write(lockfd, buf, strlen(buf));
syslog(LOG_DEBUG, "send_job(%s, %s, %d): have lock", job->job_printer,
job->job_server, job->job_id);
connection_failed = 0;
if ((nd = net_open(job->job_server, 5)) < 0) {
connection_failed = 1;
if ((nd != NETWORK_ERROR_UNKNOWN) && (nd != NETWORK_ERROR_PORT))
job_destroy(job);
else
(void) ftruncate(lockfd, lock_size);
(void) close(lockfd);
return ((nd == NETWORK_ERROR_UNKNOWN) ||
(nd == NETWORK_ERROR_PORT) ? SEND_RETRY : SEND_ABORT);
}
if (net_send_message(nd, "%c%s\n", XFER_REQUEST, job->job_printer)
!= 0) {
(void) net_close(nd);
syslog(LOG_WARNING,
"send_job failed job %d (%s@%s) check status\n",
job->job_id, job->job_printer, job->job_server);
error_notify(job->job_user, job->job_id,
gettext("\t\t check queue for (%s@%s)\n"),
job->job_printer, job->job_server);
(void) ftruncate(lockfd, lock_size);
(void) close(lockfd);
return (SEND_RETRY);
}
syslog(LOG_DEBUG, "send_job(%s, %s, %d): send data", job->job_printer,
job->job_server, job->job_id);
if ((p = getpwnam(job->job_user)) != NULL) {
/*
* 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);
(void) initgroups(job->job_user, p->pw_gid);
(void) setgid(p->pw_gid);
(void) setreuid(p->pw_uid, -1);
(void) seteuid(p->pw_uid);
}
for (tmp = 0; job->job_df_list[tmp] != NULL; tmp++)
if ((rc = sendfile(job->job_df_list[tmp], nd, XFER_DATA)) < 0)
break; /* there was an error, quit now */
tmp = errno;
if (p != NULL) {
/*
* lose the supplemental groups and elevate our effective
* uid to root so that we can destroy jobs and/or become
* other job owners later on.
*/
(void) seteuid(0);
(void) initgroups("root", 1);
}
errno = tmp;
if (rc < 0) {
if (errno == ENOENT) {
(void) net_close(nd);
error_notify(job->job_user, job->job_id, gettext(
"\t\tdata removed before transfer, job "
"canceled.\n\t\tTry \"lp -c\" or \"lpr\"\n"));
job_destroy(job);
(void) close(lockfd);
return (SEND_ABORT);
} else if (errno == EACCES) {
/* probably trying to circumvent file security */
(void) net_close(nd);
error_notify(job->job_user, job->job_id, gettext(
"\t\tunable to read job data.\n"));
job_destroy(job);
(void) close(lockfd);
return (SEND_ABORT);
} else {
(void) net_close(nd);
(void) ftruncate(lockfd, lock_size);
error_notify(job->job_user, job->job_id,
gettext("\t\t check queue for (%s@%s)\n"),
job->job_printer, job->job_server);
(void) close(lockfd);
return (SEND_RETRY);
}
}
if (sendfile(job->job_cf, nd, XFER_CONTROL) < 0) {
(void) net_send_message(nd, "%c\n", XFER_CLEANUP);
(void) net_close(nd);
(void) ftruncate(lockfd, lock_size);
error_notify(job->job_user, job->job_id,
gettext("\t\t check queue for (%s@%s)\n"),
job->job_printer, job->job_server);
(void) close(lockfd);
return (SEND_RETRY);
}
syslog(LOG_DEBUG, "send_job(%s, %s, %d): complete", job->job_printer,
job->job_server, job->job_id);
(void) net_close(nd);
job_destroy(job);
(void) close(lockfd);
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
xfer_daemon()
{
job_t **list = NULL;
int i,
rc;
closelog();
closefrom(0);
_notified = 1;
(void) open("/dev/null", O_RDONLY);
(void) open("/dev/null", O_WRONLY);
(void) dup(1);
(void) setuid(0);
(void) setsid();
openlog("printd", LOG_PID, LOG_LPR);
if (fork() != 0)
exit(0);
if ((i = get_lock(MASTER_LOCK, 1)) < 0)
exit(0);
(void) chdir(SPOOL_DIR);
while ((list = job_list_append(NULL, NULL, NULL, SPOOL_DIR)) != NULL) {
job_t **tmp;
syslog(LOG_DEBUG, "got the queue...");
for (tmp = list; *tmp != NULL; tmp++) {
/*
* 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().
*/
rc = send_job(*tmp);
if ((rc != 0) && (rc != SEND_ABORT)) {
char *s = strdup((*tmp)->job_server);
char *p = strdup((*tmp)->job_printer);
if (rc != SEND_ABORT) /* already free */
job_free(*tmp);
for (tmp++; ((*tmp != NULL) &&
(strcmp(s, (*tmp)->job_server) == 0));
tmp++)
if ((connection_failed == 0) &&
(strcmp(p,
(*tmp)->job_printer) == 0))
job_free(*tmp);
else
break;
tmp--;
free(s);
free(p);
}
}
free(list);
/* look for more work to do before we sleep */
if ((list = job_list_append(NULL, NULL, NULL,
SPOOL_DIR)) != NULL) {
(void) list_iterate((void **)list, (VFUNC_T)job_free);
free(list);
(void) sleep(60);
}
}
syslog(LOG_DEBUG, "daemon exiting...");
}
static void
append_string(char *s, va_list ap)
{
char *buf = va_arg(ap, char *);
if (strlen(buf) != 0)
(void) strcat(buf, " ");
(void) strcat(buf, s);
}
static char *
build_string(char **list)
{
int size = 0;
char *buf = NULL;
if (list != NULL) {
size = list_iterate((void **)list, (VFUNC_T)strlen);
size += 16;
buf = malloc(size);
(void) memset(buf, NULL, size);
(void) list_iterate((void **)list, (VFUNC_T)append_string, buf);
}
return (buf);
}
#define ADD_PRIMATIVE(job, primative, value) \
if ((job != NULL) && (value != NULL)) \
(void) job_primative(job, primative, value);
#define ADD_SVR4_PRIMATIVE(job, primative, value) \
if ((job != NULL) && (value != NULL)) (void) job_svr4_primative(job, \
primative, value);
#define ADD_INT_PRIMATIVE(job, primative, value, ok) \
if ((job != NULL) && (value != ok)) { \
(void) sprintf(buf, "%d", value); \
(void) job_primative(job, primative, buf); \
}
#define ADD_SVR4_INT_PRIMATIVE(job, primative, value, ok) \
if ((job != NULL) && (value != ok)) { \
(void) sprintf(buf, "%d", value); \
(void) job_svr4_primative(job, primative, \
buf); \
}
#define OPTION_ERROR(option, value) \
if (value != NULL) \
(void) fprintf(stderr, gettext("\tignoring: %s %s\n"), \
option, value);
#define OPTION_ERROR_INT(option, value) \
if (value != -1) \
(void) fprintf(stderr, gettext("\tignoring: %s %d\n"), \
option, value);
/*
* Main program. if called with "lpr" use the BSD syntax, if called
* with "lp", use the SYSV syntax. If called by any other name,
* become a transfer daemon. In the lpr/lp case, build a job and
* 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.
*/
main(int ac, char *av[])
{
ns_bsd_addr_t *binding = NULL;
int numFiles = 0,
queueStdin = 0,
exit_code = 0;
char *program,
*user,
hostname[128],
buf[BUFSIZ];
job_t *job;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if ((program = strrchr(av[0], '/')) == NULL)
program = av[0];
else
program++;
openlog(program, LOG_PID, LOG_LPR);
/*
* 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.
*/
(void) signal(SIGPIPE, sigpipe_handler);
if (check_client_spool(NULL) < 0) {
(void) fprintf(stderr,
gettext("couldn't validate local spool area (%s)\n"),
SPOOL_DIR);
return (-1);
}
if (strcmp(program, "lpr") == 0) {
if ((printer = getenv((const char *)"PRINTER")) == NULL)
printer = getenv((const char *)"LPDEST");
bsd_options(ac, av);
} else if (strcmp(program, "lp") == 0) {
if ((printer = getenv((const char *)"LPDEST")) == NULL)
printer = getenv((const char *)"PRINTER");
sysv_options(ac, av);
} else {
xfer_daemon();
return (0);
}
if (printer == NULL) {
ns_printer_t *pobj = ns_printer_get_name(NS_NAME_DEFAULT, NULL);
if (pobj != NULL) {
printer = ns_get_value_string(NS_KEY_USE, pobj);
ns_printer_destroy(pobj);
}
if (printer == NULL)
printer = NS_NAME_DEFAULT;
}
if (printer == NULL) {
(void) fprintf(stderr, gettext("No default destination\n"));
return (1);
}
if ((binding = ns_bsd_addr_get_name(printer)) == NULL) {
(void) fprintf(stderr, gettext("%s: unknown printer\n"),
printer);
return (1);
}
if (rejecting(binding->printer) != 0) {
(void) fprintf(stderr, gettext(
"%s: requests are not being accepted\n"),
printer);
return (1);
}
(void) sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
#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],
ppath[MAXPATHLEN];
(void) snprintf(ppath, sizeof (ppath),
"/etc/lp/printers/%s/configuration", binding->printer);
(void) snprintf(cpath, sizeof (cpath),
"/etc/lp/classes/%s", binding->printer);
if ((strcasecmp(binding->server, hostname) == 0) &&
((access(ppath, F_OK) == 0) ||
(access(cpath, F_OK) == 0)) &&
(access(OLD_LP, F_OK) == 0)) {
printer = binding->printer;
submit_local_lp(program, ac, av);
}
}
#endif
if ((job = job_create(strdup(binding->printer), strdup(binding->server),
SPOOL_DIR)) == NULL) {
syslog(LOG_ERR,
"Error creating job: check spooling directory: %s",
SPOOL_DIR);
(void) fprintf(stderr, gettext(
"Error creating job: check spooling directory: %s\n"),
SPOOL_DIR);
return (-1);
}
(void) umask(0);
user = get_user_name();
ADD_PRIMATIVE(job, CF_HOST, hostname);
ADD_PRIMATIVE(job, CF_USER, user);
ADD_PRIMATIVE(job, CF_TITLE, title);
if (banner != 0) {
if (jobName != NULL) {
ADD_PRIMATIVE(job, CF_JOBNAME, jobName);
} else if ((av[optind] == NULL) ||
(strcmp(av[optind], "-") == 0)) {
ADD_PRIMATIVE(job, CF_JOBNAME, "standard input");
} else {
ADD_PRIMATIVE(job, CF_JOBNAME, av[optind]);
}
ADD_PRIMATIVE(job, CF_CLASS, (class ? class : hostname));
ADD_PRIMATIVE(job, CF_PRINT_BANNER, user);
}
if (mail != 0) {
(void) snprintf(buf, sizeof (buf), "%s@%s", user, hostname);
ADD_PRIMATIVE(job, CF_MAIL, buf);
}
ADD_INT_PRIMATIVE(job, CF_INDENT, indent, -1); /* ASCII */
ADD_INT_PRIMATIVE(job, CF_WIDTH, width, -1);
if ((type == CF_PRINT_DVI) || (type == CF_PRINT_DROFF) ||
(type == CF_PRINT_TROFF)) {
ADD_PRIMATIVE(job, CF_FONT_TROFF_R, fontR);
ADD_PRIMATIVE(job, CF_FONT_TROFF_I, fontI);
ADD_PRIMATIVE(job, CF_FONT_TROFF_B, fontB);
ADD_PRIMATIVE(job, CF_FONT_TROFF_S, fontS);
}
if (binding->extension == NULL)
binding->extension = "";
if ((strcasecmp(binding->extension, NS_EXT_SOLARIS) == 0) ||
(strcasecmp(binding->extension, NS_EXT_GENERIC) == 0)) {
/* RFC1179 compliant don't get this */
syslog(LOG_DEBUG, "main(): add Solaris extensions");
ADD_PRIMATIVE(job, CF_SYSV_OPTION, build_string(s5options));
ADD_SVR4_INT_PRIMATIVE(job, CF_SYSV_PRIORITY, priority, -1);
ADD_SVR4_PRIMATIVE(job, CF_SYSV_FORM, form);
ADD_SVR4_PRIMATIVE(job, CF_SYSV_CHARSET, charset);
ADD_SVR4_PRIMATIVE(job, CF_SYSV_NOTIFICATION, notification);
ADD_SVR4_PRIMATIVE(job, CF_SYSV_HANDLING, handling);
ADD_SVR4_PRIMATIVE(job, CF_SYSV_PAGES, pages);
if (s5type != NULL) {
ADD_SVR4_PRIMATIVE(job, CF_SYSV_TYPE, s5type);
} else if (internal_type != NULL)
ADD_SVR4_PRIMATIVE(job, CF_SYSV_TYPE, internal_type);
ADD_SVR4_PRIMATIVE(job, CF_SYSV_MODE, build_string(mode));
} else if (strcasecmp(binding->extension, NS_EXT_HPUX) == 0) {
syslog(LOG_DEBUG, "main(): add HP-UX extensions");
if (s5options != NULL) {
char buf[BUFSIZ];
(void) snprintf(buf, sizeof (buf), " O%s",
build_string(s5options));
ADD_PRIMATIVE(job, CF_SOURCE_NAME, buf);
}
} else {
if ((s5options != NULL) || (form != NULL) || (pages != NULL) ||
(charset != NULL) || (notification != NULL) ||
(handling != NULL) || (s5type != NULL) || (mode != NULL) ||
(priority != -1))
(void) fprintf(stderr, gettext(
"Warning: %s not configured to handle all lp options:\n"),
printer);
OPTION_ERROR("-o", build_string(s5options));
OPTION_ERROR("-f", form);
OPTION_ERROR("-P", pages);
OPTION_ERROR("-S", charset);
OPTION_ERROR("-p", notification);
OPTION_ERROR("-H", handling);
OPTION_ERROR("-T", s5type);
OPTION_ERROR("-y", build_string(mode));
OPTION_ERROR_INT("-q", priority);
}
syslog(LOG_DEBUG, "main(): add files");
if (ac-optind > 0) {
while (optind < ac)
if (strcmp(av[optind++], "-") == 0)
queueStdin++;
else if (job_add_data_file(job, av[optind-1], title,
type, copies, linked, delete) < 0) {
switch (errno) {
case EISDIR:
(void) fprintf(stderr, gettext(
"%s: not a regular file\n"),
av[optind-1]);
break;
case ESRCH:
(void) fprintf(stderr, gettext(
"%s: empty file\n"),
av[optind-1]);
break;
case ENFILE:
(void) fprintf(stderr, gettext(
"too many files, ignoring %s\n"),
av[optind-1]);
break;
case EOVERFLOW:
(void) fprintf(stderr, gettext(
"%s: largefile (>= 2GB), ignoring\n"),
av[optind-1]);
break;
default:
perror(av[optind-1]);
}
exit_code = -1;
} else
numFiles++;
} else
queueStdin++;
if (queueStdin != 0) {
char *name;
/* standard input */
if ((name = stdin_to_file()) != NULL) {
if (job_add_data_file(job, name,
gettext("standard input"),
type, copies, 0, 0) < 0) {
switch (errno) {
case ESRCH:
(void) fprintf(stderr, gettext(
"standard input empty\n"));
break;
case ENFILE:
(void) fprintf(stderr, gettext(
"too many files, ignoring standard input\n"));
break;
default:
perror(name);
}
exit_code = -1;
} else
numFiles++;
(void) unlink(name);
free(name);
}
}
if (numFiles == 0)
return (-1);
if (seteuid(0) < 0)
perror("seteuid(0)");
(void) signal(SIGBUS, sigbus_handler);
(void) chdir(SPOOL_DIR);
(void) job_store(job);
if (suppress == 0)
if (numFiles == 1)
(void) printf(
gettext("request id is %s-%d (%d file)\n"),
printer, job->job_id, numFiles);
else
(void) printf(
gettext("request id is %s-%d (%d files)\n"),
printer, job->job_id, numFiles);
(void) fflush(stdout);
/*
* bgolden 10/2/96
* BUG 1264627
* when executed from xemacs, a sighup will kill
* the child before the job is sent. ignore the signal
*/
(void) signal(SIGHUP, SIG_IGN);
switch (fork()) { /* for immediate response */
case -1:
syslog(LOG_ERR, "fork() failed: %m");
break;
case 0:
break;
default:
return (exit_code);
}
if (send_job(job) == SEND_RETRY) {
syslog(LOG_DEBUG, "main(): transfer failed");
start_daemon(0);
}
else
syslog(LOG_DEBUG, "main(): transfer succeeded");
return (0);
}