summaryrefslogtreecommitdiff
path: root/usr/src/cmd/print/gateway/printd.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/print/gateway/printd.c')
-rw-r--r--usr/src/cmd/print/gateway/printd.c336
1 files changed, 336 insertions, 0 deletions
diff --git a/usr/src/cmd/print/gateway/printd.c b/usr/src/cmd/print/gateway/printd.c
new file mode 100644
index 0000000000..d94ea5cbcb
--- /dev/null
+++ b/usr/src/cmd/print/gateway/printd.c
@@ -0,0 +1,336 @@
+/*
+ * 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 2006 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 <ns.h>
+#include <network.h>
+#include <misc.h>
+#include <list.h>
+#include <job.h>
+
+static int connection_failed = 0;
+
+/*
+ * 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.
+ */
+
+#define SEND_RETRY -1
+#define SEND_ABORT -2
+
+/*ARGSUSED*/
+static void sigpipe_handler(int i)
+{
+ syslog(LOG_ERR, "Warning: Received SIGPIPE; continuing");
+ (void) signal(SIGPIPE, sigpipe_handler);
+}
+
+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);
+ (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);
+ job_destroy(job);
+ (void) close(lockfd);
+ return (SEND_ABORT);
+ } else if (errno == EACCES) {
+ /* probably trying to circumvent file security */
+ (void) net_close(nd);
+ job_destroy(job);
+ (void) close(lockfd);
+ return (SEND_ABORT);
+ } else {
+ (void) net_close(nd);
+ (void) ftruncate(lockfd, lock_size);
+ (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);
+ (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);
+
+ (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...");
+}
+
+int
+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);
+ }
+
+ xfer_daemon();
+
+ exit(0);
+}