diff options
Diffstat (limited to 'usr/src/lib/print/libprint/common/job.c')
-rw-r--r-- | usr/src/lib/print/libprint/common/job.c | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/usr/src/lib/print/libprint/common/job.c b/usr/src/lib/print/libprint/common/job.c new file mode 100644 index 0000000000..b379648d26 --- /dev/null +++ b/usr/src/lib/print/libprint/common/job.c @@ -0,0 +1,591 @@ +/* + * 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" + +/*LINTLIBRARY*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <sys/mman.h> +#include <sys/systeminfo.h> +#include <syslog.h> +#include <errno.h> +#include <libintl.h> +#include <grp.h> + +#include <job.h> +#include <misc.h> +#include <list.h> + + +#define MAX_RETRIES (5) + +static char *_control_file_prefix = CONTROL_FILE_PREFIX; +static char *_xfer_file_prefix = XFER_FILE_PREFIX; + + +/* + * _job_unlink_data_file() will unlink the path for the jobfile passed in. + * this is only to be used with job_destroy() so it can iterate through + * the job_df_list. + */ +static int +_job_unlink_data_file(jobfile_t *file) +{ + syslog(LOG_DEBUG, "_job_unlink_data_file(%s)", + ((file != NULL) ? file->jf_spl_path : "NULL")); + if (file && file->jf_spl_path) + return (unlink(file->jf_spl_path)); + else + return (-1); +} + +/* + * + */ +static void +_job_file_free(jobfile_t *file) +{ + if (file == NULL) + return; + if (file->jf_spl_path != NULL) free(file->jf_spl_path); + if (file->jf_src_path != NULL) free(file->jf_src_path); + if (file->jf_name != NULL) free(file->jf_name); + if (file->jf_data != NULL) { + if (file->jf_mmapped) + (void) munmap(file->jf_data, file->jf_size); + else + free(file->jf_data); + } + free(file); +} + + +/* + * + */ +static void +_vjob_file_free(jobfile_t *file) +{ + _job_file_free(file); +} + + +/* + * job_free() frees up memory mmapped for malloced + * being used by the structure. + */ +void +job_free(job_t *job) +{ + if (job == NULL) + return; + + syslog(LOG_DEBUG, "job_free(%d, %s, %s)", job->job_id, + (job->job_printer ? job->job_printer : "NULL"), + (job->job_server ? job->job_server : "NULL")); + + if (job->job_printer) free(job->job_printer); + if (job->job_server) free(job->job_server); + if (job->job_user) free(job->job_user); + if (job->job_host) free(job->job_host); + if (job->job_cf) + _job_file_free(job->job_cf); + (void) list_iterate((void *)job->job_df_list, (VFUNC_T)_vjob_file_free); + + if (job->job_df_list) + free(job->job_df_list); + + if (job->job_spool_dir) + free(job->job_spool_dir); + + free(job); +} +void +job_destroy(job_t *job) +{ + char *name = NULL; + jobfile_t *cf; + + if (job == NULL) + return; + + syslog(LOG_DEBUG, "job_destroy(%d, %s, %s)", job->job_id, + job->job_printer, job->job_server); + if (chdir(job->job_spool_dir) < 0) + return; + (void) list_iterate((void *)job->job_df_list, + (VFUNC_T)_job_unlink_data_file); + + /* lose privilege temporarily */ + (void) seteuid(get_user_id(job->job_user)); + + if ((cf = job->job_cf) != NULL) { + for (name = cf->jf_data; name != NULL; + name = strchr(name, '\n')) { + if (name[0] == '\n') + name++; + if (name[0] == CF_UNLINK) { + struct stat st; + char *path = strcdup(&name[1], '\n'), + *p; + + if (stat(path, &st) < 0) { + free(path); + continue; + } + + if (st.st_uid == getuid()) { + (void) unlink(path); + free(path); + continue; + } + + p = strdup(path); + if ((p = strrchr(p, '/')) != NULL) + *++p = NULL; + + if (access(p, W_OK) == 0) + (void) unlink(path); + free(path); + } + } + } + (void) seteuid(0); /* get back privilege */ + + (void) unlink(cf->jf_src_path); + (void) _job_unlink_data_file(cf); + job_free(job); +} + +static int +get_job_from_cfile(jobfile_t *file, char *cFile, char *xFile, job_t *tmp) +{ + jobfile_t *file1; + int n_cnt; + char *p, *cfp; + + /* map in the control data */ + if ((file->jf_size = map_in_file(cFile, &file->jf_data, 0)) <= 0) { + syslog(LOG_INFO, "could not read control file (%s): %m, " + "canceling %d destined for %s:%s", + (file->jf_spl_path ? file->jf_spl_path:"NULL"), + tmp->job_id, + (tmp->job_server ? tmp->job_server : "NULL"), + (tmp->job_printer ? tmp->job_printer : "NULL")); + return (0); + } + file->jf_mmapped = 1; + tmp->job_cf = file; + + /* look for usr, host, & data files */ + + /* + * Bugid 4137904 - "File Name" can be + * anywhere in control file. + * Bugid 4179341 - "File Name" can be missing + * in control file. + * Keep a separate pointer to the control file. + * When a CF_UNLINK entry is found use the second + * pointer to search for a corresponding 'N' entry. + * The behavior is to associate the first CF_UNLINK + * entry with the first 'N' entry and so on. + * Note: n_cnt is only used to determine if we + * should test for 'N' at the beginning of + * the file. + */ + cfp = file->jf_data; + n_cnt = 0; + for (p = file->jf_data - 1; p != NULL; p = strchr(p, '\n')) { + switch (*(++p)) { + case CF_USER: + tmp->job_user = strcdup(++p, '\n'); + break; + case CF_HOST: + tmp->job_host = strcdup(++p, '\n'); + break; + case CF_UNLINK: + if ((file1 = calloc(1, sizeof (*file))) == NULL) { + syslog(LOG_DEBUG, "cf_unlink: calloc() failed"); + munmap(file->jf_data, file->jf_size); + file->jf_mmapped = 0; + return (0); + } + file1->jf_src_path = strdup(xFile); + file1->jf_spl_path = strcdup(++p, '\n'); + file1->jf_size = file_size(file1->jf_spl_path); + + if (cfp != NULL) { + /* + * Beginning of file. Check for first + * character == 'N' + */ + if ((n_cnt == 0) && (*cfp == 'N')) { + cfp++; + n_cnt++; + } else { + cfp = strstr(cfp, "\nN"); + if (cfp != NULL) { + cfp += 2; + n_cnt++; + } + } + if (cfp != NULL) { + file1->jf_name = strcdup(cfp, '\n'); + /* + * Move cfp to end of line or + * set to NULL if end of file. + */ + cfp = strchr(cfp, '\n'); + } + } + tmp->job_df_list = (jobfile_t **)list_append((void **) + tmp->job_df_list, (void *)file1); + break; + } + } + if (tmp->job_df_list == NULL) { + munmap(file->jf_data, file->jf_size); + file->jf_mmapped = 0; + return (0); + } + + return (1); +} + +/* + * job_retrieve() will retrieve the disk copy of a job associated with the + * transfer file name passed in. It returns a pointer to a job structure + * or a NULL if the job was not on disk. + */ +job_t * +job_retrieve(char *xFile, char *spool) +{ + int retry_cnt = 0; + char *s; + jobfile_t *file; + char cFile[BUFSIZ]; + char buf[BUFSIZ]; + int fd; + flock_t flk; + job_t *tmp; + + syslog(LOG_DEBUG, "job_retrieve(%s)", xFile); + if ((tmp = (job_t *)calloc(1, sizeof (*tmp))) == NULL) { + return (NULL); + } + + if ((file = calloc(1, sizeof (*file))) == NULL) { + free(tmp); + return (NULL); + } + + flk.l_type = F_RDLCK; + flk.l_whence = 1; + flk.l_start = 0; + flk.l_len = 0; + + (void) memset(buf, NULL, sizeof (buf)); + /* get job id, from binding file name */ + (void) strlcpy(buf, xFile + strlen(_xfer_file_prefix) + 1, + sizeof (buf)); + + buf[3] = NULL; + tmp->job_id = atoi(buf); + + /* Construct data file and control file names */ + (void) strlcpy(cFile, _control_file_prefix, sizeof (cFile)); + (void) strlcat(cFile, xFile + strlen(_xfer_file_prefix), + sizeof (cFile)); + + /* remove data file and control file whenever xFile is removed */ + if ((fd = open(xFile, O_RDONLY)) < 0) { + syslog(LOG_DEBUG, "job_retrieve(%s) open failed errno=%d", + xFile, errno); + if (get_job_from_cfile(file, cFile, xFile, tmp)) + job_destroy(tmp); + free(file); + free(tmp); + (void) unlink(xFile); + (void) unlink(cFile); + return (NULL); + } + + /* + * If failed to get a lock on the file, just return NULL. It will + * be retried later. + */ + if ((fcntl(fd, F_SETLK, &flk)) < 0) { + syslog(LOG_DEBUG, "job_retrieve(%s) lock failed errno=%d", + xFile, errno); + close(fd); + free(file); + free(tmp); + return (NULL); + } + + /* + * Retry a few times if we failed to read or read returns 0, just + * to make sure we tried hard before giving up. In practice, + * there were cases of read() returning 0. To handle that + * scenario just try a few times. + */ + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + if ((read(fd, buf, sizeof (buf))) > 0) { + close(fd); + if ((s = strtok(buf, ":\n")) != NULL) + tmp->job_server = strdup(s); + if ((s = strtok(NULL, ":\n")) != NULL) + tmp->job_printer = strdup(s); + syslog(LOG_DEBUG, "job_retrieve(%s) success - %s:%s", + xFile, tmp->job_server, tmp->job_printer); + break; + } + } + /* + * If failed to read after MAX_RETRIES, return NULL and remove xFile, + * and cFile. + */ + if (retry_cnt == MAX_RETRIES) { + syslog(LOG_DEBUG, "job_retrieve(%s) unsuccessful", xFile); + if (get_job_from_cfile(file, cFile, xFile, tmp)) + job_destroy(tmp); + free(file); + free(tmp); + (void) unlink(xFile); + (void) unlink(cFile); + return (NULL); + } + + file->jf_src_path = strdup(xFile); + file->jf_spl_path = strdup(cFile); + + if (!get_job_from_cfile(file, cFile, xFile, tmp)) { + (void) unlink(file->jf_spl_path); /* control file */ + (void) unlink(file->jf_src_path); /* binding file */ + free(file->jf_src_path); + free(file->jf_spl_path); + free(file); + free(tmp); + return (NULL); + } + + tmp->job_spool_dir = strdup(spool); + return (tmp); +} + + +/* + * job_compar() compare 2 jobs for creation time ordering + */ +static int +job_compar(job_t **j1, job_t **j2) +{ + int server; + int printer; + struct stat s1, + s2; + jobfile_t *f1 = (*j1)->job_cf, + *f2 = (*j2)->job_cf; + + /* + * If there is a null value, assume the job submitted remotely. + * Jobs submitted remotely take precedence over those submitted + * from the server. + */ + if (((*j1)->job_server) == NULL || ((*j1)->job_printer) == NULL || + ((*j1)->job_cf) == NULL) + return (-1); + + else if ((*j2)->job_server == NULL || (*j2)->job_printer == NULL || + (*j2)->job_cf == NULL) + return (1); + + server = strcmp((*j1)->job_server, (*j2)->job_server); + printer = strcmp((*j1)->job_printer, (*j2)->job_printer); + + if (server != 0) + return (server); + if (printer != 0) + return (printer); + + if ((stat(f1->jf_spl_path, &s1) == 0) && + (stat(f2->jf_spl_path, &s2) == 0)) + return (s1.st_ctime - s2.st_ctime); + + return (0); +} + + +/* + * job_list_append() reads all of the jobs associated with the printer passed + * in and appends them to the list of jobs passed in. The returned result + * is a new list of jobs containing all jobs passed in and jobs for the + * printer specified. If the printer is NULL, all jobs for all printers + * are added to the list. + */ +job_t ** +job_list_append(job_t **list, char *printer, char *server, char *spool) +{ + struct dirent *d; + DIR *dirp; + job_t *job; + int i, found = 0; + + syslog(LOG_DEBUG, "job_list_append(0x%x, %s, %s)", list, + ((printer != NULL) ? printer : "NULL"), + ((server != NULL) ? server : "NULL")); + + /* + * 4239765 - in.lpd segfaults performing strcmp() + * in job_list_append() + */ + if (server == NULL) { + server = ""; + } + + if ((dirp = opendir(spool)) == NULL) + return (NULL); + + /* should use scandir */ + while ((d = readdir(dirp)) != NULL) { + if (strncmp(d->d_name, _xfer_file_prefix, + strlen(_xfer_file_prefix)) != 0) + continue; + if ((job = job_retrieve(d->d_name, spool)) == NULL) + continue; + syslog(LOG_DEBUG, "job_printer is (%s:%s)", + job->job_printer, job->job_server); + + found = 0; + + if ((printer == NULL) || + ((strcmp(printer, job->job_printer) == 0) && + (strcmp(server, job->job_server) == 0))) { + if (list) { + for (i = 0; list[i] != NULL; i++) { + if ((list[i]->job_cf != NULL) && + (job->job_cf != NULL) && + (strcmp(list[i]->job_cf->jf_spl_path, + job->job_cf->jf_spl_path) == 0)) + found = 1; + } + } /* if (list) */ + + if (!found) + list = (job_t **)list_append((void **)list, + (void *)job); + } + } /* while */ + + /* count the length of the list for qsort() */ + if (list) { + for (i = 0; list[i] != NULL; i++) + ; + + qsort(list, i, sizeof (job_t *), + (int(*)(const void *, const void *))job_compar); + } + (void) closedir(dirp); + return (list); +} + + + +/* + * + * Shared routines for Canceling jobs. + * + */ + + +/* + * vjob_match_attribute() checks to see if the attribute passed in + * matches the the user or id of the job passed in via stdargs. This is + * intended for use with list_iterate(). + */ +int +vjob_match_attribute(char *attribute, va_list ap) +{ + job_t *job = va_arg(ap, job_t *); + + if ((strcmp(attribute, job->job_user) == 0) || + (job->job_id == atoi(attribute))) + return (1); + else + return (0); +} + + +/* + * vjob_job() determines if the job passed in is for the printer and server + * of the cancel request, and if it is from the user requesting or the + * user is root, it checsk the attributes. If the job matches all of this + * it cancels the job and prints a message. It is intented to be called + * by list_iterate(). + */ +int +vjob_cancel(job_t *job, va_list ap) +{ + int killed_process = 0; + int lock; + char *user = va_arg(ap, char *), + *printer = va_arg(ap, char *), + *server = va_arg(ap, char *), + **list = va_arg(ap, char **); + + syslog(LOG_DEBUG, "vjob_cancel((%s, %s, %d), %s, %s, %s)", + job->job_printer, job->job_server, job->job_id, user, printer, + server); + if (((strcmp(user, "root") == 0) || + (strcmp(user, job->job_user) == 0)) && + ((strcmp(printer, job->job_printer) == 0) && + (strcmp(server, job->job_server) == 0))) { + if (list_iterate((void **)list, + (VFUNC_T)vjob_match_attribute, job) != 0) { + while ((lock = get_lock(job->job_cf->jf_src_path, + 0)) < 0) { + (void) kill_process(job->job_cf->jf_src_path); + killed_process = 1; + } + job_destroy(job); + syslog(LOG_DEBUG, "\t%s-%d: canceled\n", printer, + job->job_id); + (void) printf( + (char *)gettext("\t%s-%d: canceled\n"), printer, + (int)job->job_id); + close(lock); + } + } + return (killed_process); +} |