xref: /illumos-gate/usr/src/cmd/lp/lib/papi/job.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*LINTLIBRARY*/
27 
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <libintl.h>
32 #include <pwd.h>
33 #include <sys/stat.h>
34 #include <papi_impl.h>
35 
36 /*
37  * for an older application that may have been linked with a pre-v1.0
38  * PAPI implementation.
39  */
40 papi_status_t
41 papiAttributeListAdd(papi_attribute_t ***attrs, int flags, char *name,
42 		papi_attribute_value_type_t type, papi_attribute_value_t *value)
43 {
44 	return (papiAttributeListAddValue(attrs, flags, name, type, value));
45 }
46 
47 #ifdef LP_USE_PAPI_ATTR
48 static papi_status_t psm_modifyAttrsFile(papi_attribute_t **attrs, char *file);
49 static papi_status_t psm_modifyAttrsList(char *file, papi_attribute_t **attrs,
50 					papi_attribute_t ***newAttrs);
51 #endif
52 
53 
54 void
55 papiJobFree(papi_job_t job)
56 {
57 	job_t *tmp = (job_t *)job;
58 
59 	if (tmp != NULL) {
60 		papiAttributeListFree(tmp->attributes);
61 		free(tmp);
62 	}
63 }
64 
65 void
66 papiJobListFree(papi_job_t *jobs)
67 {
68 	if (jobs != NULL) {
69 		int i;
70 
71 		for (i = 0; jobs[i] != NULL; i++) {
72 			papiJobFree(jobs[i]);
73 		}
74 		free(jobs);
75 	}
76 }
77 
78 papi_attribute_t **
79 papiJobGetAttributeList(papi_job_t job)
80 {
81 	job_t *tmp = (job_t *)job;
82 
83 	if (tmp != NULL)
84 		return (tmp->attributes);
85 
86 	return (NULL);
87 }
88 
89 char *
90 papiJobGetPrinterName(papi_job_t job)
91 {
92 	job_t *tmp = (job_t *)job;
93 	char *result = NULL;
94 
95 	if (tmp != NULL)
96 		papiAttributeListGetString(tmp->attributes, NULL,
97 		    "printer-name", &result);
98 
99 	return (result);
100 }
101 
102 int32_t
103 papiJobGetId(papi_job_t job)
104 {
105 	job_t *tmp = (job_t *)job;
106 	int result = -1;
107 
108 	if (tmp != NULL)
109 		papiAttributeListGetInteger(tmp->attributes, NULL, "job-id",
110 		    &result);
111 
112 	return (result);
113 }
114 
115 static REQUEST *
116 create_request(papi_service_t svc, char *printer, papi_attribute_t **attributes)
117 {
118 	REQUEST *r;
119 
120 	if ((r = calloc(1, sizeof (*r))) != NULL) {
121 		char *hostname = NULL;
122 
123 		r->priority = -1;
124 		r->destination = printer_name_from_uri_id(printer, -1);
125 
126 		papiAttributeListGetString(attributes, NULL,
127 		    "job-originating-host-name", &hostname);
128 
129 		if (hostname == NULL) {
130 			char host[BUFSIZ];
131 
132 			if (gethostname(host, sizeof (host)) == 0)
133 				papiAttributeListAddString(&attributes,
134 				    PAPI_ATTR_REPLACE,
135 				    "job-originating-host-name",
136 				    host);
137 		}
138 
139 		job_attributes_to_lpsched_request(svc, r, attributes);
140 	}
141 
142 	return (r);
143 }
144 
145 static papi_status_t
146 authorized(service_t *svc, int32_t id)
147 {
148 	papi_status_t result = PAPI_NOT_AUTHORIZED;	/* assume the worst */
149 	char file[32];
150 	REQUEST *r;
151 
152 	snprintf(file, sizeof (file), "%d-0", id);
153 	if ((r = getrequest(file)) != NULL) {
154 		uid_t uid = getuid();
155 		struct passwd *pw = NULL;
156 		char *user = "intruder";	/* assume an intruder */
157 
158 		if ((pw = getpwuid(uid)) != NULL)
159 			user = pw->pw_name;	/* use the process owner */
160 
161 		if ((uid == 0) || (uid == 71)) { /* root/lp can forge this */
162 			papi_status_t s;
163 			s = papiAttributeListGetString(svc->attributes, NULL,
164 			    "user-name", &user);
165 			if (s != PAPI_OK)	/* true root/lp are almighty */
166 				result = PAPI_OK;
167 		}
168 
169 		if (result != PAPI_OK) {
170 			if (strcmp(user, r->user) == 0)
171 				result = PAPI_OK;
172 			else {
173 				/*
174 				 * user and r->user might contain the
175 				 * host info also
176 				 */
177 				char *token1 = strtok(r->user, "@");
178 				char *token2 = strtok(NULL, "@");
179 				char *token3 = strtok(user, "@");
180 				char *token4 = strtok(NULL, "@");
181 
182 				/*
183 				 * token1 and token3 contain usernames
184 				 * token2 and token4 contain hostnames
185 				 */
186 				if ((token1 == NULL) || (token3 == NULL))
187 					result = PAPI_NOT_AUTHORIZED;
188 				else if ((token4 != NULL) &&
189 				    (strcmp(token4, "localhost") == 0) &&
190 				    (strcmp(token3, "root") == 0) ||
191 				    (strcmp(token3, "lp") == 0)) {
192 					/*
193 					 * root/lp user on server can
194 					 * cancel any requset
195 					 */
196 					result = PAPI_OK;
197 				} else if (strcmp(token1, token3) == 0) {
198 					/*
199 					 * usernames are same
200 					 * compare the hostnames
201 					 */
202 					if ((token4 != NULL) &&
203 					    (token2 != NULL) &&
204 					    (strcmp(token4, "localhost") ==
205 					    0)) {
206 						/*
207 						 * Its server machine
208 						 */
209 						static char host[256];
210 						if (gethostname(host,
211 						    sizeof (host)) == 0) {
212 							if ((host != NULL) &&
213 							    (strcmp(host,
214 							    token2) == 0))
215 								result =
216 								    PAPI_OK;
217 						}
218 
219 					} else if ((token4 != NULL) &&
220 					    (token2 != NULL) &&
221 					    (strcmp(token4, token2) == 0)) {
222 						result = PAPI_OK;
223 					} else if ((token4 == NULL) &&
224 					    (token2 != NULL)) {
225 						/*
226 						 * When the request is sent from
227 						 * client to server using ipp
228 						 * token4 is NULL
229 						 */
230 						result = PAPI_OK;
231 					}
232 				}
233 			}
234 		}
235 
236 		freerequest(r);
237 	} else
238 		result = PAPI_NOT_FOUND;
239 
240 	return (result);
241 }
242 
243 static papi_status_t
244 copy_file(char *from, char *to)
245 {
246 	int ifd, ofd;
247 	char buf[BUFSIZ];
248 	int rc;
249 
250 	if ((ifd = open(from, O_RDONLY)) < 0)
251 		return (PAPI_DOCUMENT_ACCESS_ERROR);
252 
253 	if ((ofd = open(to, O_WRONLY)) < 0) {
254 		close(ifd);
255 		return (PAPI_NOT_POSSIBLE);
256 	}
257 
258 	while ((rc = read(ifd, buf, sizeof (buf))) > 0)
259 		write(ofd, buf, rc);
260 
261 	close(ifd);
262 	close(ofd);
263 
264 	return (PAPI_OK);
265 }
266 
267 
268 #ifdef LP_USE_PAPI_ATTR
269 /*
270  * *****************************************************************************
271  *
272  * Description: Create a file containing all the attributes in the attribute
273  *              list passed to this function.
274  *              This file is then passed through lpsched and given to either
275  *              a slow-filter or to the printer's interface script to process
276  *              the attributes.
277  *
278  * Parameters:  attrs - list of attributes and their values
279  *              file  - file pathname to create and put the attributes into.
280  *
281  * *****************************************************************************
282  */
283 
284 static papi_status_t
285 psm_copy_attrsToFile(papi_attribute_t **attrs, char *file)
286 
287 {
288 	papi_status_t result = PAPI_OK;
289 
290 	if ((attrs != NULL) && (*attrs != NULL)) {
291 		FILE *out = NULL;
292 
293 		if ((out = fopen(file, "w")) != NULL) {
294 			papiAttributeListPrint(out, attrs, "");
295 			fclose(out);
296 		} else {
297 			result = PAPI_NOT_POSSIBLE;
298 		}
299 	}
300 
301 	return (result);
302 } /* psm_copy_attrsToFile */
303 
304 
305 /*
306  * *****************************************************************************
307  *
308  * Description: Modify the given attribute 'file' with the attributes from the
309  *              'attrs' list. Attributes already in the file will be replaced
310  *              with the new value. New attributes will be added into the file.
311  *
312  * Parameters:  attrs - list of attributes and their values
313  *              file  - file pathname to create and put the attributes into.
314  *
315  * *****************************************************************************
316  */
317 
318 static papi_status_t
319 psm_modifyAttrsFile(papi_attribute_t **attrs, char *file)
320 
321 {
322 	papi_status_t result = PAPI_OK;
323 	papi_attribute_t **newAttrs = NULL;
324 	struct stat   tmpBuf;
325 	FILE *fd = NULL;
326 
327 	if ((attrs != NULL) && (*attrs != NULL) && (file != NULL)) {
328 
329 		/*
330 		 * check file exist before try to modify it, if it doesn't
331 		 * exist assume there is an error
332 		 */
333 		if (stat(file, &tmpBuf) == 0) {
334 			/*
335 			 * if file is currently empty just write the given
336 			 * attributes to the file otherwise exact the attributes
337 			 * from the file and modify them accordingly before
338 			 * writing them back to the file
339 			 */
340 			if (tmpBuf.st_size == 0) {
341 				newAttrs = (papi_attribute_t **)attrs;
342 
343 				fd = fopen(file, "w");
344 				if (fd != NULL) {
345 					papiAttributeListPrint(fd,
346 							newAttrs, "");
347 					fclose(fd);
348 				} else {
349 					result = PAPI_NOT_POSSIBLE;
350 				}
351 			} else {
352 				result =
353 				    psm_modifyAttrsList(file, attrs, &newAttrs);
354 
355 				fd = fopen(file, "w");
356 				if (fd != NULL) {
357 					papiAttributeListPrint(fd,
358 								newAttrs, "");
359 					fclose(fd);
360 				} else {
361 					result = PAPI_NOT_POSSIBLE;
362 				}
363 
364 				papiAttributeListFree(newAttrs);
365 			}
366 		} else {
367 			result = PAPI_NOT_POSSIBLE;
368 		}
369 	}
370 
371 	return (result);
372 } /* psm_modifyAttrsFile */
373 
374 
375 /*
376  * *****************************************************************************
377  *
378  * Description: Extracts the attributes in the given attribute 'file' and
379  *              creates a new list 'newAttrs' containing the modified list of
380  *              attributes.
381  *
382  * Parameters:  file  - pathname of file containing attributes to be modified
383  *              attrs - list of attributes and their values to modify
384  *              newAttrs - returns the modified list of attributes
385  *
386  * *****************************************************************************
387  */
388 
389 static papi_status_t
390 psm_modifyAttrsList(char *file, papi_attribute_t **attrs,
391     papi_attribute_t ***newAttrs)
392 
393 {
394 	papi_status_t result = PAPI_OK;
395 	papi_attribute_t  *nextAttr = NULL;
396 	papi_attribute_value_t  **values = NULL;
397 	void *iter = NULL;
398 	FILE *fd = NULL;
399 	register int fD = 0;
400 	char aBuff[200];
401 	char *a = NULL;
402 	char *p = NULL;
403 	int count = 0;
404 	int n = 0;
405 
406 	fd = fopen(file, "r");
407 	if (fd != NULL) {
408 		fD = fileno(fd);
409 		a = &aBuff[0];
410 		p = &aBuff[0];
411 		count = read(fD, &aBuff[0], sizeof (aBuff) - 1);
412 		while ((result == PAPI_OK) && (count > 0)) {
413 			aBuff[count+n] = '\0';
414 			if (count == sizeof (aBuff) - n - 1) {
415 				p = strrchr(aBuff, '\n');
416 				if (p != NULL) {
417 					/* terminate at last complete line */
418 					*p = '\0';
419 				}
420 			}
421 			result = papiAttributeListFromString(
422 				newAttrs, PAPI_ATTR_EXCL, aBuff);
423 
424 			if (result == PAPI_OK) {
425 				/*
426 				 * handle any part lines and then read the next
427 				 * buffer from the file
428 				 */
429 				n = 0;
430 				if (p != a) {
431 					p++; /* skip NL */
432 					n = sizeof (aBuff) - 1 - (p - a);
433 					strncpy(aBuff, p, n);
434 				}
435 				count = read(fD, &aBuff[n],
436 					sizeof (aBuff) - n - 1);
437 				p = &aBuff[0];
438 			}
439 		}
440 		fclose(fd);
441 	}
442 
443 	/* now modify the attribute list with the new attributes in 'attrs' */
444 
445 	nextAttr = papiAttributeListGetNext((papi_attribute_t **)attrs, &iter);
446 	while ((result == PAPI_OK) && (nextAttr != NULL)) {
447 		values = nextAttr->values;
448 
449 		if ((values != NULL) && (*values != NULL)) {
450 			result = papiAttributeListAddValue(newAttrs,
451 						    PAPI_ATTR_REPLACE,
452 						    nextAttr->name,
453 						    nextAttr->type, *values);
454 			values++;
455 		}
456 
457 		while ((result == PAPI_OK) &&
458 			(values != NULL) && (*values != NULL)) {
459 			result = papiAttributeListAddValue(newAttrs,
460 						    PAPI_ATTR_APPEND,
461 						    nextAttr->name,
462 						    nextAttr->type, *values);
463 			values++;
464 		}
465 		nextAttr =
466 		    papiAttributeListGetNext((papi_attribute_t **)attrs, &iter);
467 	}
468 
469 	return (result);
470 } /* papi_modifyAttrsList() */
471 #endif
472 
473 
474 papi_status_t
475 papiJobSubmit(papi_service_t handle, char *printer,
476 		papi_attribute_t **job_attributes,
477 		papi_job_ticket_t *job_ticket,
478 		char **files, papi_job_t *job)
479 {
480 	papi_status_t status;
481 	service_t *svc = handle;
482 	struct stat statbuf;
483 	job_t *j;
484 	int file_no;
485 	char *request_id = NULL;
486 	REQUEST *request;
487 	int i;
488 	char *c;
489 	char *tmp = NULL;
490 	char lpfile[BUFSIZ];
491 
492 	if ((svc == NULL) || (printer == NULL) || (files == NULL) ||
493 	    (job == NULL))
494 		return (PAPI_BAD_ARGUMENT);
495 
496 	if (job_ticket != NULL)
497 		return (PAPI_OPERATION_NOT_SUPPORTED);
498 
499 	if (files != NULL)
500 		for (file_no = 0; files[file_no] != NULL; file_no++) {
501 			if (access(files[file_no], R_OK) < 0) {
502 				detailed_error(svc,
503 				    gettext("Cannot access file: %s: %s"),
504 				    files[file_no], strerror(errno));
505 				return (PAPI_BAD_ARGUMENT);
506 			}
507 			if (stat(files[file_no], &statbuf) < 0) {
508 				detailed_error(svc,
509 				    gettext("Cannot access file: %s: %s"),
510 				    files[file_no], strerror(errno));
511 				return (PAPI_DOCUMENT_ACCESS_ERROR);
512 			}
513 			if (statbuf.st_size == 0) {
514 				detailed_error(svc,
515 				    gettext("Zero byte (empty) file: %s"),
516 				    files[file_no]);
517 				return (PAPI_BAD_ARGUMENT);
518 			}
519 		}
520 
521 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
522 		return (PAPI_TEMPORARY_ERROR);
523 
524 	/* file_no + 1 for the control file (-0) */
525 	status = lpsched_alloc_files(svc, file_no + 1, &request_id);
526 	if (status != PAPI_OK)
527 		return (status);
528 
529 	request = create_request(svc, (char *)printer,
530 	    (papi_attribute_t **)job_attributes);
531 
532 	for (i = 0; files[i] != NULL; i++) {
533 		papi_status_t status;
534 		snprintf(lpfile, sizeof (lpfile), "%s%s-%d",
535 		    "/var/spool/lp/temp/", request_id, i+1);
536 		status = copy_file(files[i], lpfile);
537 		if (status != PAPI_OK) {
538 			detailed_error(svc,
539 			    gettext("unable to copy: %s -> %s: %s"),
540 			    files[i], lpfile, strerror(errno));
541 				freerequest(request);
542 			return (PAPI_DEVICE_ERROR);
543 		}
544 		addlist(&(request->file_list), lpfile);
545 	}
546 
547 #ifdef LP_USE_PAPI_ATTR
548 	/*
549 	 * store the job attributes in the PAPI job attribute file that was
550 	 * created by lpsched_alloc_files(), the attributes will then pass
551 	 * through lpsched and be given to the slow-filters and the printer's
552 	 * interface script to process them
553 	 */
554 	snprintf(lpfile, sizeof (lpfile), "%s%s-%s",
555 	    "/var/spool/lp/temp/", request_id, LP_PAPIATTRNAME);
556 	status = psm_copy_attrsToFile(job_attributes, lpfile);
557 	if (status != PAPI_OK) {
558 		detailed_error(svc, "unable to copy attributes to file: %s: %s",
559 		    lpfile, strerror(errno));
560 		return (PAPI_DEVICE_ERROR);
561 	}
562 #endif
563 
564 	/* store the meta-data file */
565 	snprintf(lpfile, sizeof (lpfile), "%s-0", request_id);
566 	if (putrequest(lpfile, request) < 0) {
567 		detailed_error(svc, gettext("unable to save request: %s: %s"),
568 		    lpfile, strerror(errno));
569 		freerequest(request);
570 		return (PAPI_DEVICE_ERROR);
571 	}
572 
573 	status = lpsched_commit_job(svc, lpfile, &tmp);
574 	if (status != PAPI_OK) {
575 		unlink(lpfile);
576 		freerequest(request);
577 		return (status);
578 	}
579 
580 	lpsched_request_to_job_attributes(request, j);
581 	freerequest(request);
582 
583 	if ((c = strrchr(tmp, '-')) != NULL)
584 		c++;
585 	papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
586 	    "job-id", atoi(c));
587 	papiAttributeListAddString(&j->attributes, PAPI_ATTR_REPLACE,
588 	    "job-uri", tmp);
589 
590 	return (PAPI_OK);
591 }
592 
593 papi_status_t
594 papiJobSubmitByReference(papi_service_t handle, char *printer,
595 		papi_attribute_t **job_attributes,
596 		papi_job_ticket_t *job_ticket,
597 		char **files, papi_job_t *job)
598 {
599 	service_t *svc = handle;
600 	struct stat statbuf;
601 	job_t *j;
602 	int file_no;
603 	short status;
604 	char *request_id = NULL;
605 	REQUEST *request;
606 	char *c;
607 	char *tmp = NULL;
608 	char lpfile[BUFSIZ];
609 	char **file_list = NULL;
610 
611 	if ((svc == NULL) || (printer == NULL) || (files == NULL) ||
612 	    (job == NULL))
613 		return (PAPI_BAD_ARGUMENT);
614 
615 	if (job_ticket != NULL)
616 		return (PAPI_OPERATION_NOT_SUPPORTED);
617 
618 	if (files != NULL)
619 		for (file_no = 0; files[file_no] != NULL; file_no++) {
620 			if (access(files[file_no], R_OK) < 0) {
621 				detailed_error(svc,
622 				    gettext("Cannot access file: %s: %s"),
623 				    files[file_no], strerror(errno));
624 				return (PAPI_DOCUMENT_ACCESS_ERROR);
625 			}
626 			if (stat(files[file_no], &statbuf) < 0) {
627 				detailed_error(svc,
628 				    gettext("Cannot access file: %s: %s"),
629 				    files[file_no], strerror(errno));
630 				return (PAPI_DOCUMENT_ACCESS_ERROR);
631 			}
632 			if (statbuf.st_size == 0) {
633 				detailed_error(svc,
634 				    gettext("Zero byte (empty) file: %s"),
635 				    files[file_no]);
636 				return (PAPI_BAD_ARGUMENT);
637 			}
638 
639 			if (files[file_no][0] != '/') {
640 				char path[MAXPATHLEN];
641 
642 				if (getcwd(path, sizeof (path)) == NULL) {
643 					detailed_error(svc, gettext(
644 					    "getcwd for file: %s: %s"),
645 					    files[file_no],
646 					    strerror(errno));
647 					return (PAPI_DOCUMENT_ACCESS_ERROR);
648 				}
649 				strlcat(path, "/", sizeof (path));
650 				if (strlcat(path, files[file_no], sizeof (path))
651 				    >= sizeof (path)) {
652 					detailed_error(svc, gettext(
653 					    "pathname too long: %s"),
654 					    files[file_no]);
655 					return (PAPI_DOCUMENT_ACCESS_ERROR);
656 				}
657 				addlist(&file_list, path);
658 			} else
659 				addlist(&file_list, (char *)files[file_no]);
660 		}
661 
662 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
663 		return (PAPI_TEMPORARY_ERROR);
664 
665 	/* 1 for the control file (-0) */
666 	status = lpsched_alloc_files(svc, 1, &request_id);
667 	if (status != PAPI_OK)
668 		return (status);
669 
670 	request = create_request(svc, (char *)printer,
671 	    (papi_attribute_t **)job_attributes);
672 	request->file_list = file_list;
673 
674 #ifdef LP_USE_PAPI_ATTR
675 	/*
676 	 * store the job attributes in the PAPI job attribute file that was
677 	 * created by lpsched_alloc_files(), the attributes will then pass
678 	 * through lpsched and be given to the slow-filters and the printer's
679 	 * interface script to process them
680 	 */
681 	snprintf(lpfile, sizeof (lpfile), "%s%s-%s",
682 	    "/var/spool/lp/temp/", request_id, LP_PAPIATTRNAME);
683 	status = psm_copy_attrsToFile(job_attributes, lpfile);
684 	if (status != PAPI_OK) {
685 		detailed_error(svc, "unable to copy attributes to file: %s: %s",
686 		    lpfile, strerror(errno));
687 		return (PAPI_DEVICE_ERROR);
688 	}
689 #endif
690 
691 	/* store the meta-data file */
692 	snprintf(lpfile, sizeof (lpfile), "%s-0", request_id);
693 	if (putrequest(lpfile, request) < 0) {
694 		detailed_error(svc, gettext("unable to save request: %s: %s"),
695 		    lpfile, strerror(errno));
696 		freerequest(request);
697 		return (PAPI_DEVICE_ERROR);
698 	}
699 
700 	status = lpsched_commit_job(svc, lpfile, &tmp);
701 	if (status != PAPI_OK) {
702 		unlink(lpfile);
703 		freerequest(request);
704 		return (status);
705 	}
706 
707 	lpsched_request_to_job_attributes(request, j);
708 
709 	freerequest(request);
710 
711 	if ((c = strrchr(tmp, '-')) != NULL)
712 		c++;
713 	papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
714 	    "job-id", atoi(c));
715 	papiAttributeListAddString(&j->attributes, PAPI_ATTR_REPLACE,
716 	    "job-uri", tmp);
717 
718 	return (PAPI_OK);
719 }
720 
721 papi_status_t
722 papiJobValidate(papi_service_t handle, char *printer,
723 		papi_attribute_t **job_attributes,
724 		papi_job_ticket_t *job_ticket,
725 		char **files, papi_job_t *job)
726 {
727 	papi_status_t status;
728 	papi_attribute_t **attributes = NULL;
729 	int i;
730 
731 	papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE,
732 	    "job-hold-until", "indefinite");
733 	for (i = 0; job_attributes[i]; i++)
734 		list_append(&attributes, job_attributes[i]);
735 
736 	status = papiJobSubmitByReference(handle, printer,
737 	    (papi_attribute_t **)attributes,
738 	    job_ticket, files, job);
739 	if (status == PAPI_OK) {
740 		int id = papiJobGetId(*job);
741 
742 		if (id != -1)
743 			papiJobCancel(handle, printer, id);
744 	}
745 
746 	attributes[1] = NULL;	/* after attr[0], they are in another list */
747 	papiAttributeListFree(attributes);
748 
749 	return (status);
750 }
751 
752 papi_status_t
753 papiJobStreamOpen(papi_service_t handle, char *printer,
754 		papi_attribute_t **job_attributes,
755 		papi_job_ticket_t *job_ticket, papi_stream_t *stream)
756 {
757 	papi_status_t status;
758 	service_t *svc = handle;
759 	job_stream_t *s = NULL;
760 	char *request_id = NULL;
761 	char lpfile[BUFSIZ];
762 
763 	if ((svc == NULL) || (printer == NULL) || (stream == NULL))
764 		return (PAPI_BAD_ARGUMENT);
765 
766 	if (job_ticket != NULL)
767 		return (PAPI_OPERATION_NOT_SUPPORTED);
768 
769 	if ((*stream = s = calloc(1, sizeof (*s))) == NULL)
770 		return (PAPI_TEMPORARY_ERROR);
771 
772 	/* 1 for data, 1 for the meta-data (-0) */
773 	status = lpsched_alloc_files(svc, 2, &request_id);
774 	if (status != PAPI_OK)
775 		return (status);
776 
777 	s->request = create_request(svc, (char *)printer,
778 	    (papi_attribute_t **)job_attributes);
779 	snprintf(lpfile, sizeof (lpfile), "/var/spool/lp/temp/%s-1",
780 	    request_id);
781 	s->fd = open(lpfile, O_WRONLY);
782 	addlist(&(s->request->file_list), lpfile);
783 
784 #ifdef LP_USE_PAPI_ATTR
785 	/*
786 	 * store the job attributes in the PAPI job attribute file that was
787 	 * created by lpsched_alloc_files(), the attributes will then pass
788 	 * through lpsched and be given to the slow-filters and the printer's
789 	 * interface script to process them
790 	 */
791 	snprintf(lpfile, sizeof (lpfile), "%s%s-%s",
792 	    "/var/spool/lp/temp/", request_id, LP_PAPIATTRNAME);
793 	status = psm_copy_attrsToFile(job_attributes, lpfile);
794 	if (status != PAPI_OK) {
795 		detailed_error(svc, "unable to copy attributes to file: %s: %s",
796 		    lpfile, strerror(errno));
797 		close(s->fd);
798 		free(s);
799 		return (PAPI_DEVICE_ERROR);
800 	}
801 #endif
802 
803 	/* store the meta-data file */
804 	snprintf(lpfile, sizeof (lpfile), "%s-0", request_id);
805 	s->meta_data_file = strdup(lpfile);
806 	if (putrequest(lpfile, s->request) < 0) {
807 		detailed_error(svc, gettext("unable to save request: %s: %s"),
808 		    lpfile, strerror(errno));
809 		s->request = NULL;
810 		return (PAPI_DEVICE_ERROR);
811 	}
812 
813 	return (PAPI_OK);
814 }
815 
816 papi_status_t
817 papiJobStreamWrite(papi_service_t handle,
818 		papi_stream_t stream, void *buffer, size_t buflen)
819 {
820 	service_t *svc = handle;
821 	job_stream_t *s = stream;
822 
823 	if ((svc == NULL) || (stream == NULL) || (buffer == NULL))
824 		return (PAPI_BAD_ARGUMENT);
825 
826 	if (write(s->fd, buffer, buflen) != buflen)
827 		return (PAPI_DEVICE_ERROR);
828 
829 	return (PAPI_OK);
830 }
831 papi_status_t
832 papiJobStreamClose(papi_service_t handle,
833 		papi_stream_t stream, papi_job_t *job)
834 {
835 	papi_status_t status = PAPI_OK;
836 	service_t *svc = handle;
837 	job_stream_t *s = stream;
838 	job_t *j = NULL;
839 	char *tmp = NULL, *c;
840 
841 	if ((svc == NULL) || (stream == NULL) || (job == NULL))
842 		return (PAPI_BAD_ARGUMENT);
843 
844 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
845 		return (PAPI_TEMPORARY_ERROR);
846 
847 	close(s->fd);
848 
849 	lpsched_request_to_job_attributes(s->request, j);
850 
851 	if (s->meta_data_file != NULL) {
852 		status = lpsched_commit_job(svc, s->meta_data_file, &tmp);
853 		if (status != PAPI_OK) {
854 			unlink(s->meta_data_file);
855 			return (status);
856 		}
857 		if ((c = strrchr(tmp, '-')) != NULL)
858 			c++;
859 		papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
860 		    "job-id", atoi(c));
861 		papiAttributeListAddString(&j->attributes, PAPI_ATTR_REPLACE,
862 		    "job-uri", tmp);
863 		free(s->meta_data_file);
864 	}
865 	freerequest(s->request);
866 	free(s);
867 
868 	return (PAPI_OK);
869 }
870 
871 papi_status_t
872 papiJobQuery(papi_service_t handle, char *printer, int32_t job_id,
873 		char **requested_attrs,
874 		papi_job_t *job)
875 {
876 	service_t *svc = handle;
877 	job_t *j;
878 	char *dest;
879 	char req_id[32];
880 	short rc;
881 	char *form = NULL,
882 	    *request_id = NULL,
883 	    *charset = NULL,
884 	    *user = NULL,
885 	    *slabel = NULL,
886 	    *file = NULL;
887 	time_t date = 0;
888 	size_t size = 0;
889 	short  rank = 0,
890 	    state = 0;
891 
892 	if ((handle == NULL) || (printer == NULL) || (job_id < 0))
893 		return (PAPI_BAD_ARGUMENT);
894 
895 	dest = printer_name_from_uri_id(printer, job_id);
896 	snprintf(req_id, sizeof (req_id), "%s-%d", dest, job_id);
897 	free(dest);
898 
899 	rc = snd_msg(svc, S_INQUIRE_REQUEST_RANK, 0, "", "", req_id, "", "");
900 	if (rc < 0)
901 		return (PAPI_SERVICE_UNAVAILABLE);
902 
903 	if (rcv_msg(svc, R_INQUIRE_REQUEST_RANK, &rc, &request_id,
904 	    &user, &slabel, &size, &date, &state, &dest, &form,
905 	    &charset, &rank, &file) < 0) {
906 		detailed_error(svc,
907 		    gettext("failed to read response from scheduler"));
908 		return (PAPI_DEVICE_ERROR);
909 	}
910 
911 	if ((request_id == NULL) || (request_id[0] == NULL))
912 		return (PAPI_NOT_FOUND);
913 
914 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
915 		return (PAPI_TEMPORARY_ERROR);
916 
917 	job_status_to_attributes(j, request_id, user, slabel, size, date, state,
918 	    dest, form, charset, rank, file);
919 
920 	snprintf(req_id, sizeof (req_id), "%d-0", job_id);
921 	lpsched_read_job_configuration(svc, j, req_id);
922 
923 	return (PAPI_OK);
924 }
925 
926 papi_status_t
927 papiJobMove(papi_service_t handle, char *printer, int32_t job_id,
928 		char *destination)
929 {
930 	papi_status_t result = PAPI_OK;
931 	long bits;
932 	service_t *svc = handle;
933 	char req_id[64];
934 	char *queue;
935 	char *user = NULL;
936 
937 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
938 	    (destination == NULL))
939 		return (PAPI_BAD_ARGUMENT);
940 
941 	queue = printer_name_from_uri_id(printer, job_id);
942 	snprintf(req_id, sizeof (req_id), "%s-%d", queue, job_id);
943 	free(queue);
944 
945 	if (papiAttributeListGetString(svc->attributes, NULL, "user-name",
946 	    &user) == PAPI_OK) {
947 		REQUEST *r = getrequest(req_id);
948 
949 		if ((r != NULL) && (r->user != NULL) &&
950 		    (strcmp(r->user, user) != 0))
951 			result = PAPI_NOT_AUTHORIZED;
952 		freerequest(r);
953 	}
954 
955 	if (result == PAPI_OK) {
956 		short status = MOK;
957 		char *dest = printer_name_from_uri_id(destination, -1);
958 
959 		if ((snd_msg(svc, S_MOVE_REQUEST, req_id, dest) < 0) ||
960 		    (rcv_msg(svc, R_MOVE_REQUEST, &status, &bits) < 0))
961 			status = MTRANSMITERR;
962 
963 		free(dest);
964 
965 		result = lpsched_status_to_papi_status(status);
966 	}
967 
968 	return (result);
969 }
970 
971 papi_status_t
972 papiJobCancel(papi_service_t handle, char *printer, int32_t job_id)
973 {
974 	papi_status_t result = PAPI_OK;
975 	service_t *svc = handle;
976 	char req_id[64];
977 	char *dest;
978 	char *user = NULL;
979 
980 	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
981 		return (PAPI_BAD_ARGUMENT);
982 
983 	dest = printer_name_from_uri_id(printer, job_id);
984 	snprintf(req_id, sizeof (req_id), "%s-%d", dest, job_id);
985 	free(dest);
986 
987 	if (papiAttributeListGetString(svc->attributes, NULL, "user-name",
988 	    &user) == PAPI_OK) {
989 		REQUEST *r = getrequest(req_id);
990 
991 		if ((result = authorized(handle, job_id)) != PAPI_OK)
992 			result = PAPI_NOT_AUTHORIZED;
993 
994 		if ((r != NULL) && (r->user != NULL) &&
995 		    (strcmp(r->user, user) != 0))
996 			result = PAPI_NOT_AUTHORIZED;
997 		freerequest(r);
998 	}
999 
1000 	if (result == PAPI_OK) {
1001 		short status = MOK;
1002 
1003 		if ((snd_msg(svc, S_CANCEL_REQUEST, req_id) < 0) ||
1004 		    (rcv_msg(svc, R_CANCEL_REQUEST, &status) < 0))
1005 			status = MTRANSMITERR;
1006 
1007 		result = lpsched_status_to_papi_status(status);
1008 	}
1009 
1010 	return (result);
1011 }
1012 
1013 papi_status_t
1014 hold_release_job(papi_service_t handle, char *printer,
1015 		int32_t job_id, int flag)
1016 {
1017 	papi_status_t status;
1018 	service_t *svc = handle;
1019 	REQUEST *r = NULL;
1020 	char *file;
1021 	char *dest;
1022 
1023 	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
1024 		return (PAPI_BAD_ARGUMENT);
1025 
1026 	if ((status = authorized(svc, job_id)) != PAPI_OK)
1027 		return (status);
1028 
1029 	dest = printer_name_from_uri_id(printer, job_id);
1030 	status = lpsched_start_change(svc, dest, job_id, &file);
1031 	if (status != PAPI_OK)
1032 		return (status);
1033 
1034 	if ((r = getrequest(file)) != NULL) {
1035 		r->actions &= ~ACT_RESUME;
1036 		switch (flag) {
1037 		case 0:
1038 			r->actions |= ACT_HOLD;
1039 			break;
1040 		case 1:
1041 			r->actions |= ACT_RESUME;
1042 			break;
1043 		case 2:
1044 			r->actions |= ACT_IMMEDIATE;
1045 			break;
1046 		}
1047 		if (putrequest(file, r) < 0) {
1048 			detailed_error(svc,
1049 			    gettext("failed to write job: %s: %s"),
1050 			    file, strerror(errno));
1051 			freerequest(r);
1052 			return (PAPI_DEVICE_ERROR);
1053 		}
1054 		freerequest(r);
1055 	} else {
1056 		detailed_error(svc, gettext("failed to read job: %s: %s"),
1057 		    file, strerror(errno));
1058 		return (PAPI_DEVICE_ERROR);
1059 	}
1060 
1061 	status = lpsched_end_change(svc, dest, job_id);
1062 
1063 	return (status);
1064 }
1065 
1066 papi_status_t
1067 papiJobHold(papi_service_t handle, char *printer, int32_t job_id)
1068 {
1069 	return (hold_release_job(handle, printer, job_id, 0));
1070 }
1071 
1072 papi_status_t
1073 papiJobRelease(papi_service_t handle, char *printer, int32_t job_id)
1074 {
1075 	return (hold_release_job(handle, printer, job_id, 1));
1076 }
1077 
1078 papi_status_t
1079 papiJobPromote(papi_service_t handle, char *printer, int32_t job_id)
1080 {
1081 	return (hold_release_job(handle, printer, job_id, 2));
1082 }
1083 
1084 papi_status_t
1085 papiJobModify(papi_service_t handle, char *printer, int32_t job_id,
1086 		papi_attribute_t **attributes, papi_job_t *job)
1087 {
1088 	papi_status_t status;
1089 	job_t *j = NULL;
1090 	service_t *svc = handle;
1091 	char *file = NULL;
1092 	char *dest;
1093 	REQUEST *r = NULL;
1094 	char lpfile[BUFSIZ];
1095 
1096 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
1097 	    (attributes == NULL))
1098 		return (PAPI_BAD_ARGUMENT);
1099 
1100 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
1101 		return (PAPI_TEMPORARY_ERROR);
1102 
1103 	dest = printer_name_from_uri_id(printer, job_id);
1104 	status = lpsched_start_change(svc, dest, job_id, &file);
1105 	if (status != PAPI_OK)
1106 		return (status);
1107 
1108 	if ((r = getrequest(file)) != NULL) {
1109 		job_attributes_to_lpsched_request(handle, r,
1110 		    (papi_attribute_t **)attributes);
1111 #ifdef LP_USE_PAPI_ATTR
1112 		/*
1113 		 * store the job attributes in the PAPI job attribute file
1114 		 * that was created by the origonal job request. We need to
1115 		 * modify the attributes in the file as per the new attributes
1116 		 */
1117 		snprintf(lpfile, sizeof (lpfile), "%s%d-%s",
1118 		    "/var/spool/lp/temp/", job_id, LP_PAPIATTRNAME);
1119 		status = psm_modifyAttrsFile(attributes, lpfile);
1120 		if (status != PAPI_OK) {
1121 			detailed_error(svc,
1122 			    "unable to modify the attributes file: %s: %s",
1123 			    lpfile, strerror(errno));
1124 			return (PAPI_DEVICE_ERROR);
1125 		}
1126 #endif
1127 
1128 		if (putrequest(file, r) < 0) {
1129 			detailed_error(svc,
1130 			    gettext("failed to write job: %s: %s"),
1131 			    file, strerror(errno));
1132 			freerequest(r);
1133 			return (PAPI_DEVICE_ERROR);
1134 		}
1135 	} else {
1136 		detailed_error(svc, gettext("failed to read job: %s: %s"),
1137 		    file, strerror(errno));
1138 		return (PAPI_DEVICE_ERROR);
1139 	}
1140 
1141 	status = lpsched_end_change(svc, dest, job_id);
1142 	lpsched_request_to_job_attributes(r, j);
1143 
1144 	papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
1145 	    "job-id", job_id);
1146 
1147 	freerequest(r);
1148 
1149 	return (status);
1150 }
1151 
1152 /*
1153  * Extension to PAPI, a variation of this is slated for post-1.0
1154  */
1155 #define	DUMMY_FILE	"/var/spool/lp/fifos/FIFO"
1156 
1157 papi_status_t
1158 papiJobCreate(papi_service_t handle, char *printer,
1159 		papi_attribute_t **job_attributes,
1160 		papi_job_ticket_t *job_ticket, papi_job_t *job)
1161 {
1162 	papi_status_t status;
1163 	service_t *svc = handle;
1164 	job_t *j = NULL;
1165 	REQUEST *request;
1166 	char *request_id = NULL;
1167 	char *c;
1168 	char *tmp = NULL;
1169 	char metadata_file[MAXPATHLEN];
1170 
1171 	if ((svc == NULL) || (printer == NULL) || (job == NULL))
1172 		return (PAPI_BAD_ARGUMENT);
1173 
1174 	if (job_ticket != NULL)
1175 		return (PAPI_JOB_TICKET_NOT_SUPPORTED);
1176 
1177 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
1178 		return (PAPI_TEMPORARY_ERROR);
1179 
1180 	/* 1 for the control file (-0) */
1181 	status = lpsched_alloc_files(svc, 1, &request_id);
1182 	if (status != PAPI_OK)
1183 		return (status);
1184 
1185 	/* convert the attributes to an lpsched REQUEST structure */
1186 	request = create_request(svc, (char *)printer,
1187 	    (papi_attribute_t **)job_attributes);
1188 	if (request == NULL)
1189 		return (PAPI_TEMPORARY_ERROR);
1190 	addlist(&request->file_list, DUMMY_FILE);	/* add a dummy file */
1191 	request->actions |= ACT_HOLD;			/* hold the job */
1192 
1193 #ifdef LP_USE_PAPI_ATTR
1194 	/*
1195 	 * store the job attributes in the PAPI job attribute file that was
1196 	 * created by lpsched_alloc_files(), the attributes will then pass
1197 	 * through lpsched and be given to the slow-filters and the printer's
1198 	 * interface script to process them
1199 	 */
1200 	snprintf(metadata_file, sizeof (metadata_file), "%s%s-%s",
1201 	    "/var/spool/lp/temp/", request_id, LP_PAPIATTRNAME);
1202 	status = psm_copy_attrsToFile(job_attributes, metadata_file);
1203 	if (status != PAPI_OK) {
1204 		detailed_error(svc, "unable to copy attributes to file: %s: %s",
1205 		    metadata_file, strerror(errno));
1206 		free(request_id);
1207 		return (PAPI_DEVICE_ERROR);
1208 	}
1209 #endif
1210 
1211 	/* store the REQUEST on disk */
1212 	snprintf(metadata_file, sizeof (metadata_file), "%s-0", request_id);
1213 	free(request_id);
1214 	if (putrequest(metadata_file, request) < 0) {
1215 		detailed_error(svc, gettext("unable to save request: %s: %s"),
1216 		    metadata_file, strerror(errno));
1217 		return (PAPI_DEVICE_ERROR);
1218 	}
1219 
1220 	status = lpsched_commit_job(svc, metadata_file, &tmp);
1221 	if (status != PAPI_OK) {
1222 		unlink(metadata_file);
1223 		return (status);
1224 	}
1225 
1226 	lpsched_request_to_job_attributes(request, j);
1227 
1228 	if ((c = strrchr(tmp, '-')) != NULL)
1229 		c++;
1230 	papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
1231 	    "job-id", atoi(c));
1232 	papiAttributeListAddString(&j->attributes, PAPI_ATTR_REPLACE,
1233 	    "job-uri", tmp);
1234 
1235 	return (PAPI_OK);
1236 }
1237 
1238 papi_status_t
1239 papiJobCommit(papi_service_t handle, char *printer, int32_t id)
1240 {
1241 	papi_status_t status = PAPI_OK;
1242 	service_t *svc = handle;
1243 	REQUEST *r = NULL;
1244 	char *metadata_file;
1245 	char *dest;
1246 
1247 	if ((svc == NULL) || (printer == NULL))
1248 		return (PAPI_BAD_ARGUMENT);
1249 
1250 	dest = printer_name_from_uri_id(printer, id);
1251 	/* tell the scheduler that we want to change the job */
1252 	status = lpsched_start_change(svc, dest, id, &metadata_file);
1253 	if (status != PAPI_OK)
1254 		return (status);
1255 
1256 	if ((r = getrequest(metadata_file)) != NULL) {
1257 		r->actions &= ~ACT_RESUME;
1258 		r->actions |= ACT_RESUME;
1259 		dellist(&r->file_list, DUMMY_FILE);
1260 
1261 		if (putrequest(metadata_file, r) < 0) {
1262 			detailed_error(svc,
1263 			    gettext("failed to write job: %s: %s"),
1264 			    metadata_file, strerror(errno));
1265 			freerequest(r);
1266 			return (PAPI_DEVICE_ERROR);
1267 		}
1268 	} else {
1269 		detailed_error(svc, gettext("failed to read job: %s: %s"),
1270 		    metadata_file, strerror(errno));
1271 		return (PAPI_DEVICE_ERROR);
1272 	}
1273 
1274 	status = lpsched_end_change(svc, dest, id);
1275 	freerequest(r);
1276 
1277 	return (status);
1278 }
1279 
1280 papi_status_t
1281 papiJobStreamAdd(papi_service_t handle, char *printer, int32_t id,
1282 		papi_stream_t *stream)
1283 {
1284 	papi_status_t status;
1285 	service_t *svc = handle;
1286 	job_stream_t *s = NULL;
1287 	char *metadata_file = NULL;
1288 	char *dest;
1289 	char path[MAXPATHLEN];
1290 
1291 	/* allocate space for the stream */
1292 	if ((*stream = s = calloc(1, sizeof (*s))) == NULL)
1293 		return (PAPI_TEMPORARY_ERROR);
1294 
1295 	dest = printer_name_from_uri_id(printer, id);
1296 	/* create/open data file (only root or lp can really do this */
1297 	snprintf(path, sizeof (path), "/var/spool/lp/temp/%d-XXXXXX", id);
1298 	if ((s->fd = mkstemp(path)) < 0) {
1299 		detailed_error(svc, gettext("unable to create sink (%s): %s"),
1300 		    path, strerror(errno));
1301 		free(s);
1302 		return (PAPI_NOT_AUTHORIZED);
1303 	}
1304 
1305 	/* add data file to job */
1306 	status = lpsched_start_change(svc, dest, id, &metadata_file);
1307 	if (status != PAPI_OK) {
1308 		close(s->fd);
1309 		free(s);
1310 		unlink(path);
1311 		return (status);
1312 	}
1313 
1314 	if ((s->request = getrequest(metadata_file)) == NULL) {
1315 		detailed_error(svc, gettext("unable to load request: %s: %s"),
1316 		    metadata_file, strerror(errno));
1317 		close(s->fd);
1318 		free(s);
1319 		unlink(path);
1320 		return (PAPI_NOT_POSSIBLE);
1321 	}
1322 
1323 	addlist(&(s->request->file_list), path);
1324 
1325 	if (putrequest(metadata_file, s->request) < 0) {
1326 		detailed_error(svc, gettext("unable to save request: %s: %s"),
1327 		    metadata_file, strerror(errno));
1328 		close(s->fd);
1329 		free(s);
1330 		unlink(path);
1331 		return (PAPI_NOT_POSSIBLE);
1332 	}
1333 
1334 	status = lpsched_end_change(svc, dest, id);
1335 
1336 	if (status != PAPI_OK)
1337 		return (status);
1338 
1339 	return (PAPI_OK);
1340 }
1341