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