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