xref: /illumos-gate/usr/src/cmd/lp/lib/papi/job.c (revision a5669307eaef64af8519feb70d42f0aa0e7ec21a)
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 			if (stat(files[file_no], &statbuf) < 0) {
460 				detailed_error(svc,
461 				    gettext("Cannot access file: %s: %s"),
462 				    files[file_no], strerror(errno));
463 				return (PAPI_DOCUMENT_ACCESS_ERROR);
464 			}
465 			if (statbuf.st_size == 0) {
466 				detailed_error(svc,
467 				    gettext("Zero byte (empty) file: %s"),
468 				    files[file_no]);
469 				return (PAPI_BAD_ARGUMENT);
470 			}
471 		}
472 
473 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
474 		return (PAPI_TEMPORARY_ERROR);
475 
476 	/* file_no + 1 for the control file (-0) */
477 	status = lpsched_alloc_files(svc, file_no + 1, &request_id);
478 	if (status != PAPI_OK)
479 		return (status);
480 
481 	request = create_request(svc, (char *)printer,
482 	    (papi_attribute_t **)job_attributes);
483 
484 	for (i = 0; files[i] != NULL; i++) {
485 		papi_status_t status;
486 		snprintf(lpfile, sizeof (lpfile), "%s%s-%d",
487 		    "/var/spool/lp/temp/", request_id, i+1);
488 		status = copy_file(files[i], lpfile);
489 		if (status != PAPI_OK) {
490 			detailed_error(svc,
491 			    gettext("unable to copy: %s -> %s: %s"),
492 			    files[i], lpfile, strerror(errno));
493 				freerequest(request);
494 			return (PAPI_DEVICE_ERROR);
495 		}
496 		addlist(&(request->file_list), lpfile);
497 	}
498 
499 #ifdef LP_USE_PAPI_ATTR
500 	/*
501 	 * store the job attributes in the PAPI job attribute file that was
502 	 * created by lpsched_alloc_files(), the attributes will then pass
503 	 * through lpsched and be given to the slow-filters and the printer's
504 	 * interface script to process them
505 	 */
506 	snprintf(lpfile, sizeof (lpfile), "%s%s-%s",
507 	    "/var/spool/lp/temp/", request_id, LP_PAPIATTRNAME);
508 	status = psm_copy_attrsToFile(job_attributes, lpfile);
509 	if (status != PAPI_OK) {
510 		detailed_error(svc, "unable to copy attributes to file: %s: %s",
511 		    lpfile, strerror(errno));
512 		return (PAPI_DEVICE_ERROR);
513 	}
514 #endif
515 
516 	/* store the meta-data file */
517 	snprintf(lpfile, sizeof (lpfile), "%s-0", request_id);
518 	if (putrequest(lpfile, request) < 0) {
519 		detailed_error(svc, gettext("unable to save request: %s: %s"),
520 		    lpfile, strerror(errno));
521 		freerequest(request);
522 		return (PAPI_DEVICE_ERROR);
523 	}
524 
525 	status = lpsched_commit_job(svc, lpfile, &tmp);
526 	if (status != PAPI_OK) {
527 		unlink(lpfile);
528 		freerequest(request);
529 		return (status);
530 	}
531 
532 	lpsched_request_to_job_attributes(request, j);
533 	freerequest(request);
534 
535 	if ((c = strrchr(tmp, '-')) != NULL)
536 		c++;
537 	papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
538 	    "job-id", atoi(c));
539 	papiAttributeListAddString(&j->attributes, PAPI_ATTR_REPLACE,
540 	    "job-uri", tmp);
541 
542 	return (PAPI_OK);
543 }
544 
545 papi_status_t
546 papiJobSubmitByReference(papi_service_t handle, char *printer,
547 		papi_attribute_t **job_attributes,
548 		papi_job_ticket_t *job_ticket,
549 		char **files, papi_job_t *job)
550 {
551 	service_t *svc = handle;
552 	struct stat statbuf;
553 	job_t *j;
554 	int file_no;
555 	short status;
556 	char *request_id = NULL;
557 	REQUEST *request;
558 	char *c;
559 	char *tmp = NULL;
560 	char lpfile[BUFSIZ];
561 	char **file_list = NULL;
562 
563 	if ((svc == NULL) || (printer == NULL) || (files == NULL) ||
564 	    (job == NULL))
565 		return (PAPI_BAD_ARGUMENT);
566 
567 	if (job_ticket != NULL)
568 		return (PAPI_OPERATION_NOT_SUPPORTED);
569 
570 	if (files != NULL)
571 		for (file_no = 0; files[file_no] != NULL; file_no++) {
572 			if (access(files[file_no], R_OK) < 0) {
573 				detailed_error(svc,
574 				    gettext("Cannot access file: %s: %s"),
575 				    files[file_no], strerror(errno));
576 				return (PAPI_DOCUMENT_ACCESS_ERROR);
577 			}
578 			if (stat(files[file_no], &statbuf) < 0) {
579 				detailed_error(svc,
580 				    gettext("Cannot access file: %s: %s"),
581 				    files[file_no], strerror(errno));
582 				return (PAPI_DOCUMENT_ACCESS_ERROR);
583 			}
584 			if (statbuf.st_size == 0) {
585 				detailed_error(svc,
586 				    gettext("Zero byte (empty) file: %s"),
587 				    files[file_no]);
588 				return (PAPI_BAD_ARGUMENT);
589 			}
590 
591 			if (files[file_no][0] != '/') {
592 				char path[MAXPATHLEN];
593 
594 				if (getcwd(path, sizeof (path)) == NULL) {
595 					detailed_error(svc, gettext(
596 					    "getcwd for file: %s: %s"),
597 					    files[file_no],
598 					    strerror(errno));
599 					return (PAPI_DOCUMENT_ACCESS_ERROR);
600 				}
601 				strlcat(path, "/", sizeof (path));
602 				if (strlcat(path, files[file_no], sizeof (path))
603 				    >= sizeof (path)) {
604 					detailed_error(svc, gettext(
605 					    "pathname too long: %s"),
606 					    files[file_no]);
607 					return (PAPI_DOCUMENT_ACCESS_ERROR);
608 				}
609 				addlist(&file_list, path);
610 			} else
611 				addlist(&file_list, (char *)files[file_no]);
612 		}
613 
614 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
615 		return (PAPI_TEMPORARY_ERROR);
616 
617 	/* 1 for the control file (-0) */
618 	status = lpsched_alloc_files(svc, 1, &request_id);
619 	if (status != PAPI_OK)
620 		return (status);
621 
622 	request = create_request(svc, (char *)printer,
623 	    (papi_attribute_t **)job_attributes);
624 	request->file_list = file_list;
625 
626 #ifdef LP_USE_PAPI_ATTR
627 	/*
628 	 * store the job attributes in the PAPI job attribute file that was
629 	 * created by lpsched_alloc_files(), the attributes will then pass
630 	 * through lpsched and be given to the slow-filters and the printer's
631 	 * interface script to process them
632 	 */
633 	snprintf(lpfile, sizeof (lpfile), "%s%s-%s",
634 	    "/var/spool/lp/temp/", request_id, LP_PAPIATTRNAME);
635 	status = psm_copy_attrsToFile(job_attributes, lpfile);
636 	if (status != PAPI_OK) {
637 		detailed_error(svc, "unable to copy attributes to file: %s: %s",
638 		    lpfile, strerror(errno));
639 		return (PAPI_DEVICE_ERROR);
640 	}
641 #endif
642 
643 	/* store the meta-data file */
644 	snprintf(lpfile, sizeof (lpfile), "%s-0", request_id);
645 	if (putrequest(lpfile, request) < 0) {
646 		detailed_error(svc, gettext("unable to save request: %s: %s"),
647 		    lpfile, strerror(errno));
648 		freerequest(request);
649 		return (PAPI_DEVICE_ERROR);
650 	}
651 
652 	status = lpsched_commit_job(svc, lpfile, &tmp);
653 	if (status != PAPI_OK) {
654 		unlink(lpfile);
655 		freerequest(request);
656 		return (status);
657 	}
658 
659 	lpsched_request_to_job_attributes(request, j);
660 
661 	freerequest(request);
662 
663 	if ((c = strrchr(tmp, '-')) != NULL)
664 		c++;
665 	papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
666 	    "job-id", atoi(c));
667 	papiAttributeListAddString(&j->attributes, PAPI_ATTR_REPLACE,
668 	    "job-uri", tmp);
669 
670 	return (PAPI_OK);
671 }
672 
673 papi_status_t
674 papiJobValidate(papi_service_t handle, char *printer,
675 		papi_attribute_t **job_attributes,
676 		papi_job_ticket_t *job_ticket,
677 		char **files, papi_job_t *job)
678 {
679 	papi_status_t status;
680 	papi_attribute_t **attributes = NULL;
681 	int i;
682 
683 	papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE,
684 	    "job-hold-until", "indefinite");
685 	for (i = 0; job_attributes[i]; i++)
686 		list_append(&attributes, job_attributes[i]);
687 
688 	status = papiJobSubmitByReference(handle, printer,
689 	    (papi_attribute_t **)attributes,
690 	    job_ticket, files, job);
691 	if (status == PAPI_OK) {
692 		int id = papiJobGetId(*job);
693 
694 		if (id != -1)
695 			papiJobCancel(handle, printer, id);
696 	}
697 
698 	attributes[1] = NULL;	/* after attr[0], they are in another list */
699 	papiAttributeListFree(attributes);
700 
701 	return (status);
702 }
703 
704 papi_status_t
705 papiJobStreamOpen(papi_service_t handle, char *printer,
706 		papi_attribute_t **job_attributes,
707 		papi_job_ticket_t *job_ticket, papi_stream_t *stream)
708 {
709 	papi_status_t status;
710 	service_t *svc = handle;
711 	job_stream_t *s = NULL;
712 	char *request_id = NULL;
713 	char lpfile[BUFSIZ];
714 
715 	if ((svc == NULL) || (printer == NULL) || (stream == NULL))
716 		return (PAPI_BAD_ARGUMENT);
717 
718 	if (job_ticket != NULL)
719 		return (PAPI_OPERATION_NOT_SUPPORTED);
720 
721 	if ((*stream = s = calloc(1, sizeof (*s))) == NULL)
722 		return (PAPI_TEMPORARY_ERROR);
723 
724 	/* 1 for data, 1 for the meta-data (-0) */
725 	status = lpsched_alloc_files(svc, 2, &request_id);
726 	if (status != PAPI_OK)
727 		return (status);
728 
729 	s->request = create_request(svc, (char *)printer,
730 	    (papi_attribute_t **)job_attributes);
731 	snprintf(lpfile, sizeof (lpfile), "/var/spool/lp/temp/%s-1",
732 	    request_id);
733 	s->fd = open(lpfile, O_WRONLY);
734 	addlist(&(s->request->file_list), lpfile);
735 
736 #ifdef LP_USE_PAPI_ATTR
737 	/*
738 	 * store the job attributes in the PAPI job attribute file that was
739 	 * created by lpsched_alloc_files(), the attributes will then pass
740 	 * through lpsched and be given to the slow-filters and the printer's
741 	 * interface script to process them
742 	 */
743 	snprintf(lpfile, sizeof (lpfile), "%s%s-%s",
744 	    "/var/spool/lp/temp/", request_id, LP_PAPIATTRNAME);
745 	status = psm_copy_attrsToFile(job_attributes, lpfile);
746 	if (status != PAPI_OK) {
747 		detailed_error(svc, "unable to copy attributes to file: %s: %s",
748 		    lpfile, strerror(errno));
749 		close(s->fd);
750 		free(s);
751 		return (PAPI_DEVICE_ERROR);
752 	}
753 #endif
754 
755 	/* store the meta-data file */
756 	snprintf(lpfile, sizeof (lpfile), "%s-0", request_id);
757 	s->meta_data_file = strdup(lpfile);
758 	if (putrequest(lpfile, s->request) < 0) {
759 		detailed_error(svc, gettext("unable to save request: %s: %s"),
760 		    lpfile, strerror(errno));
761 		s->request = NULL;
762 		return (PAPI_DEVICE_ERROR);
763 	}
764 
765 	return (PAPI_OK);
766 }
767 
768 papi_status_t
769 papiJobStreamWrite(papi_service_t handle,
770 		papi_stream_t stream, void *buffer, size_t buflen)
771 {
772 	service_t *svc = handle;
773 	job_stream_t *s = stream;
774 
775 	if ((svc == NULL) || (stream == NULL) || (buffer == NULL))
776 		return (PAPI_BAD_ARGUMENT);
777 
778 	if (write(s->fd, buffer, buflen) != buflen)
779 		return (PAPI_DEVICE_ERROR);
780 
781 	return (PAPI_OK);
782 }
783 papi_status_t
784 papiJobStreamClose(papi_service_t handle,
785 		papi_stream_t stream, papi_job_t *job)
786 {
787 	papi_status_t status = PAPI_OK;
788 	service_t *svc = handle;
789 	job_stream_t *s = stream;
790 	job_t *j = NULL;
791 	char *tmp = NULL, *c;
792 
793 	if ((svc == NULL) || (stream == NULL) || (job == NULL))
794 		return (PAPI_BAD_ARGUMENT);
795 
796 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
797 		return (PAPI_TEMPORARY_ERROR);
798 
799 	close(s->fd);
800 
801 	lpsched_request_to_job_attributes(s->request, j);
802 
803 	if (s->meta_data_file != NULL) {
804 		status = lpsched_commit_job(svc, s->meta_data_file, &tmp);
805 		if (status != PAPI_OK) {
806 			unlink(s->meta_data_file);
807 			return (status);
808 		}
809 		if ((c = strrchr(tmp, '-')) != NULL)
810 			c++;
811 		papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
812 		    "job-id", atoi(c));
813 		papiAttributeListAddString(&j->attributes, PAPI_ATTR_REPLACE,
814 		    "job-uri", tmp);
815 		free(s->meta_data_file);
816 	}
817 	freerequest(s->request);
818 	free(s);
819 
820 	return (PAPI_OK);
821 }
822 
823 papi_status_t
824 papiJobQuery(papi_service_t handle, char *printer, int32_t job_id,
825 		char **requested_attrs,
826 		papi_job_t *job)
827 {
828 	service_t *svc = handle;
829 	job_t *j;
830 	char *dest;
831 	char req_id[32];
832 	short rc;
833 	char *form = NULL,
834 	    *request_id = NULL,
835 	    *charset = NULL,
836 	    *user = NULL,
837 	    *slabel = NULL,
838 	    *file = NULL;
839 	time_t date = 0;
840 	size_t size = 0;
841 	short  rank = 0,
842 	    state = 0;
843 
844 	if ((handle == NULL) || (printer == NULL) || (job_id < 0))
845 		return (PAPI_BAD_ARGUMENT);
846 
847 	dest = printer_name_from_uri_id(printer, job_id);
848 	snprintf(req_id, sizeof (req_id), "%s-%d", dest, job_id);
849 	free(dest);
850 
851 	rc = snd_msg(svc, S_INQUIRE_REQUEST_RANK, 0, "", "", req_id, "", "");
852 	if (rc < 0)
853 		return (PAPI_SERVICE_UNAVAILABLE);
854 
855 	if (rcv_msg(svc, R_INQUIRE_REQUEST_RANK, &rc, &request_id,
856 	    &user, &slabel, &size, &date, &state, &dest, &form,
857 	    &charset, &rank, &file) < 0) {
858 		detailed_error(svc,
859 		    gettext("failed to read response from scheduler"));
860 		return (PAPI_DEVICE_ERROR);
861 	}
862 
863 	if ((request_id == NULL) || (request_id[0] == NULL))
864 		return (PAPI_NOT_FOUND);
865 
866 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
867 		return (PAPI_TEMPORARY_ERROR);
868 
869 	job_status_to_attributes(j, request_id, user, slabel, size, date, state,
870 	    dest, form, charset, rank, file);
871 
872 	snprintf(req_id, sizeof (req_id), "%d-0", job_id);
873 	lpsched_read_job_configuration(svc, j, req_id);
874 
875 	return (PAPI_OK);
876 }
877 
878 papi_status_t
879 papiJobMove(papi_service_t handle, char *printer, int32_t job_id,
880 		char *destination)
881 {
882 	papi_status_t result = PAPI_OK;
883 	long bits;
884 	service_t *svc = handle;
885 	char req_id[64];
886 	char *queue;
887 	char *user = NULL;
888 
889 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
890 	    (destination == NULL))
891 		return (PAPI_BAD_ARGUMENT);
892 
893 	queue = printer_name_from_uri_id(printer, job_id);
894 	snprintf(req_id, sizeof (req_id), "%s-%d", queue, job_id);
895 	free(queue);
896 
897 	if (papiAttributeListGetString(svc->attributes, NULL, "user-name",
898 	    &user) == PAPI_OK) {
899 		REQUEST *r = getrequest(req_id);
900 
901 		if ((r != NULL) && (r->user != NULL) &&
902 		    (strcmp(r->user, user) != 0))
903 			result = PAPI_NOT_AUTHORIZED;
904 		freerequest(r);
905 	}
906 
907 	if (result == PAPI_OK) {
908 		short status = MOK;
909 		char *dest = printer_name_from_uri_id(destination, -1);
910 
911 		if ((snd_msg(svc, S_MOVE_REQUEST, req_id, dest) < 0) ||
912 		    (rcv_msg(svc, R_MOVE_REQUEST, &status, &bits) < 0))
913 			status = MTRANSMITERR;
914 
915 		free(dest);
916 
917 		result = lpsched_status_to_papi_status(status);
918 	}
919 
920 	return (result);
921 }
922 
923 papi_status_t
924 papiJobCancel(papi_service_t handle, char *printer, int32_t job_id)
925 {
926 	papi_status_t result = PAPI_OK;
927 	service_t *svc = handle;
928 	char req_id[64];
929 	char *dest;
930 	char *user = NULL;
931 
932 	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
933 		return (PAPI_BAD_ARGUMENT);
934 
935 	dest = printer_name_from_uri_id(printer, job_id);
936 	snprintf(req_id, sizeof (req_id), "%s-%d", dest, job_id);
937 	free(dest);
938 
939 	if (papiAttributeListGetString(svc->attributes, NULL, "user-name",
940 	    &user) == PAPI_OK) {
941 		REQUEST *r = getrequest(req_id);
942 
943 		if ((result = authorized(handle, job_id)) != PAPI_OK)
944 			result = PAPI_NOT_AUTHORIZED;
945 
946 		if ((r != NULL) && (r->user != NULL) &&
947 		    (strcmp(r->user, user) != 0))
948 			result = PAPI_NOT_AUTHORIZED;
949 		freerequest(r);
950 	}
951 
952 	if (result == PAPI_OK) {
953 		short status = MOK;
954 
955 		if ((snd_msg(svc, S_CANCEL_REQUEST, req_id) < 0) ||
956 		    (rcv_msg(svc, R_CANCEL_REQUEST, &status) < 0))
957 			status = MTRANSMITERR;
958 
959 		result = lpsched_status_to_papi_status(status);
960 	}
961 
962 	return (result);
963 }
964 
965 papi_status_t
966 hold_release_job(papi_service_t handle, char *printer,
967 		int32_t job_id, int flag)
968 {
969 	papi_status_t status;
970 	service_t *svc = handle;
971 	REQUEST *r = NULL;
972 	char *file;
973 	char *dest;
974 
975 	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
976 		return (PAPI_BAD_ARGUMENT);
977 
978 	if ((status = authorized(svc, job_id)) != PAPI_OK)
979 		return (status);
980 
981 	dest = printer_name_from_uri_id(printer, job_id);
982 	status = lpsched_start_change(svc, dest, job_id, &file);
983 	if (status != PAPI_OK)
984 		return (status);
985 
986 	if ((r = getrequest(file)) != NULL) {
987 		r->actions &= ~ACT_RESUME;
988 		switch (flag) {
989 		case 0:
990 			r->actions |= ACT_HOLD;
991 			break;
992 		case 1:
993 			r->actions |= ACT_RESUME;
994 			break;
995 		case 2:
996 			r->actions |= ACT_IMMEDIATE;
997 			break;
998 		}
999 		if (putrequest(file, r) < 0) {
1000 			detailed_error(svc,
1001 			    gettext("failed to write job: %s: %s"),
1002 			    file, strerror(errno));
1003 			freerequest(r);
1004 			return (PAPI_DEVICE_ERROR);
1005 		}
1006 		freerequest(r);
1007 	} else {
1008 		detailed_error(svc, gettext("failed to read job: %s: %s"),
1009 		    file, strerror(errno));
1010 		return (PAPI_DEVICE_ERROR);
1011 	}
1012 
1013 	status = lpsched_end_change(svc, dest, job_id);
1014 
1015 	return (status);
1016 }
1017 
1018 papi_status_t
1019 papiJobHold(papi_service_t handle, char *printer, int32_t job_id)
1020 {
1021 	return (hold_release_job(handle, printer, job_id, 0));
1022 }
1023 
1024 papi_status_t
1025 papiJobRelease(papi_service_t handle, char *printer, int32_t job_id)
1026 {
1027 	return (hold_release_job(handle, printer, job_id, 1));
1028 }
1029 
1030 papi_status_t
1031 papiJobPromote(papi_service_t handle, char *printer, int32_t job_id)
1032 {
1033 	return (hold_release_job(handle, printer, job_id, 2));
1034 }
1035 
1036 papi_status_t
1037 papiJobModify(papi_service_t handle, char *printer, int32_t job_id,
1038 		papi_attribute_t **attributes, papi_job_t *job)
1039 {
1040 	papi_status_t status;
1041 	job_t *j = NULL;
1042 	service_t *svc = handle;
1043 	char *file = NULL;
1044 	char *dest;
1045 	REQUEST *r = NULL;
1046 	char lpfile[BUFSIZ];
1047 
1048 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
1049 	    (attributes == NULL))
1050 		return (PAPI_BAD_ARGUMENT);
1051 
1052 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
1053 		return (PAPI_TEMPORARY_ERROR);
1054 
1055 	dest = printer_name_from_uri_id(printer, job_id);
1056 	status = lpsched_start_change(svc, dest, job_id, &file);
1057 	if (status != PAPI_OK)
1058 		return (status);
1059 
1060 	if ((r = getrequest(file)) != NULL) {
1061 		job_attributes_to_lpsched_request(handle, r,
1062 		    (papi_attribute_t **)attributes);
1063 #ifdef LP_USE_PAPI_ATTR
1064 		/*
1065 		 * store the job attributes in the PAPI job attribute file
1066 		 * that was created by the origonal job request. We need to
1067 		 * modify the attributes in the file as per the new attributes
1068 		 */
1069 		snprintf(lpfile, sizeof (lpfile), "%s%d-%s",
1070 		    "/var/spool/lp/temp/", job_id, LP_PAPIATTRNAME);
1071 		status = psm_modifyAttrsFile(attributes, lpfile);
1072 		if (status != PAPI_OK) {
1073 			detailed_error(svc,
1074 			    "unable to modify the attributes file: %s: %s",
1075 			    lpfile, strerror(errno));
1076 			return (PAPI_DEVICE_ERROR);
1077 		}
1078 #endif
1079 
1080 		if (putrequest(file, r) < 0) {
1081 			detailed_error(svc,
1082 			    gettext("failed to write job: %s: %s"),
1083 			    file, strerror(errno));
1084 			freerequest(r);
1085 			return (PAPI_DEVICE_ERROR);
1086 		}
1087 	} else {
1088 		detailed_error(svc, gettext("failed to read job: %s: %s"),
1089 		    file, strerror(errno));
1090 		return (PAPI_DEVICE_ERROR);
1091 	}
1092 
1093 	status = lpsched_end_change(svc, dest, job_id);
1094 	lpsched_request_to_job_attributes(r, j);
1095 
1096 	papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
1097 	    "job-id", job_id);
1098 
1099 	freerequest(r);
1100 
1101 	return (status);
1102 }
1103 
1104 /*
1105  * Extension to PAPI, a variation of this is slated for post-1.0
1106  */
1107 #define	DUMMY_FILE	"/var/spool/lp/fifos/FIFO"
1108 
1109 papi_status_t
1110 papiJobCreate(papi_service_t handle, char *printer,
1111 		papi_attribute_t **job_attributes,
1112 		papi_job_ticket_t *job_ticket, papi_job_t *job)
1113 {
1114 	papi_status_t status;
1115 	service_t *svc = handle;
1116 	job_t *j = NULL;
1117 	REQUEST *request;
1118 	char *request_id = NULL;
1119 	char *c;
1120 	char *tmp = NULL;
1121 	char metadata_file[MAXPATHLEN];
1122 
1123 	if ((svc == NULL) || (printer == NULL) || (job == NULL))
1124 		return (PAPI_BAD_ARGUMENT);
1125 
1126 	if (job_ticket != NULL)
1127 		return (PAPI_JOB_TICKET_NOT_SUPPORTED);
1128 
1129 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
1130 		return (PAPI_TEMPORARY_ERROR);
1131 
1132 	/* 1 for the control file (-0) */
1133 	status = lpsched_alloc_files(svc, 1, &request_id);
1134 	if (status != PAPI_OK)
1135 		return (status);
1136 
1137 	/* convert the attributes to an lpsched REQUEST structure */
1138 	request = create_request(svc, (char *)printer,
1139 	    (papi_attribute_t **)job_attributes);
1140 	if (request == NULL)
1141 		return (PAPI_TEMPORARY_ERROR);
1142 	addlist(&request->file_list, DUMMY_FILE);	/* add a dummy file */
1143 	request->actions |= ACT_HOLD;			/* hold the job */
1144 
1145 #ifdef LP_USE_PAPI_ATTR
1146 	/*
1147 	 * store the job attributes in the PAPI job attribute file that was
1148 	 * created by lpsched_alloc_files(), the attributes will then pass
1149 	 * through lpsched and be given to the slow-filters and the printer's
1150 	 * interface script to process them
1151 	 */
1152 	snprintf(metadata_file, sizeof (metadata_file), "%s%s-%s",
1153 	    "/var/spool/lp/temp/", request_id, LP_PAPIATTRNAME);
1154 	status = psm_copy_attrsToFile(job_attributes, metadata_file);
1155 	if (status != PAPI_OK) {
1156 		detailed_error(svc, "unable to copy attributes to file: %s: %s",
1157 		    metadata_file, strerror(errno));
1158 		free(request_id);
1159 		return (PAPI_DEVICE_ERROR);
1160 	}
1161 #endif
1162 
1163 	/* store the REQUEST on disk */
1164 	snprintf(metadata_file, sizeof (metadata_file), "%s-0", request_id);
1165 	free(request_id);
1166 	if (putrequest(metadata_file, request) < 0) {
1167 		detailed_error(svc, gettext("unable to save request: %s: %s"),
1168 		    metadata_file, strerror(errno));
1169 		return (PAPI_DEVICE_ERROR);
1170 	}
1171 
1172 	status = lpsched_commit_job(svc, metadata_file, &tmp);
1173 	if (status != PAPI_OK) {
1174 		unlink(metadata_file);
1175 		return (status);
1176 	}
1177 
1178 	lpsched_request_to_job_attributes(request, j);
1179 
1180 	if ((c = strrchr(tmp, '-')) != NULL)
1181 		c++;
1182 	papiAttributeListAddInteger(&j->attributes, PAPI_ATTR_REPLACE,
1183 	    "job-id", atoi(c));
1184 	papiAttributeListAddString(&j->attributes, PAPI_ATTR_REPLACE,
1185 	    "job-uri", tmp);
1186 
1187 	return (PAPI_OK);
1188 }
1189 
1190 papi_status_t
1191 papiJobCommit(papi_service_t handle, char *printer, int32_t id)
1192 {
1193 	papi_status_t status = PAPI_OK;
1194 	service_t *svc = handle;
1195 	REQUEST *r = NULL;
1196 	char *metadata_file;
1197 	char *dest;
1198 
1199 	if ((svc == NULL) || (printer == NULL))
1200 		return (PAPI_BAD_ARGUMENT);
1201 
1202 	dest = printer_name_from_uri_id(printer, id);
1203 	/* tell the scheduler that we want to change the job */
1204 	status = lpsched_start_change(svc, dest, id, &metadata_file);
1205 	if (status != PAPI_OK)
1206 		return (status);
1207 
1208 	if ((r = getrequest(metadata_file)) != NULL) {
1209 		r->actions &= ~ACT_RESUME;
1210 		r->actions |= ACT_RESUME;
1211 		dellist(&r->file_list, DUMMY_FILE);
1212 
1213 		if (putrequest(metadata_file, r) < 0) {
1214 			detailed_error(svc,
1215 			    gettext("failed to write job: %s: %s"),
1216 			    metadata_file, strerror(errno));
1217 			freerequest(r);
1218 			return (PAPI_DEVICE_ERROR);
1219 		}
1220 	} else {
1221 		detailed_error(svc, gettext("failed to read job: %s: %s"),
1222 		    metadata_file, strerror(errno));
1223 		return (PAPI_DEVICE_ERROR);
1224 	}
1225 
1226 	status = lpsched_end_change(svc, dest, id);
1227 	freerequest(r);
1228 
1229 	return (status);
1230 }
1231 
1232 papi_status_t
1233 papiJobStreamAdd(papi_service_t handle, char *printer, int32_t id,
1234 		papi_stream_t *stream)
1235 {
1236 	papi_status_t status;
1237 	service_t *svc = handle;
1238 	job_stream_t *s = NULL;
1239 	char *metadata_file = NULL;
1240 	char *dest;
1241 	char path[MAXPATHLEN];
1242 
1243 	/* allocate space for the stream */
1244 	if ((*stream = s = calloc(1, sizeof (*s))) == NULL)
1245 		return (PAPI_TEMPORARY_ERROR);
1246 
1247 	dest = printer_name_from_uri_id(printer, id);
1248 	/* create/open data file (only root or lp can really do this */
1249 	snprintf(path, sizeof (path), "/var/spool/lp/temp/%d-XXXXXX", id);
1250 	if ((s->fd = mkstemp(path)) < 0) {
1251 		detailed_error(svc, gettext("unable to create sink (%s): %s"),
1252 		    path, strerror(errno));
1253 		free(s);
1254 		return (PAPI_NOT_AUTHORIZED);
1255 	}
1256 
1257 	/* add data file to job */
1258 	status = lpsched_start_change(svc, dest, id, &metadata_file);
1259 	if (status != PAPI_OK) {
1260 		close(s->fd);
1261 		free(s);
1262 		unlink(path);
1263 		return (status);
1264 	}
1265 
1266 	if ((s->request = getrequest(metadata_file)) == NULL) {
1267 		detailed_error(svc, gettext("unable to load request: %s: %s"),
1268 		    metadata_file, strerror(errno));
1269 		close(s->fd);
1270 		free(s);
1271 		unlink(path);
1272 		return (PAPI_NOT_POSSIBLE);
1273 	}
1274 
1275 	addlist(&(s->request->file_list), path);
1276 
1277 	if (putrequest(metadata_file, s->request) < 0) {
1278 		detailed_error(svc, gettext("unable to save request: %s: %s"),
1279 		    metadata_file, strerror(errno));
1280 		close(s->fd);
1281 		free(s);
1282 		unlink(path);
1283 		return (PAPI_NOT_POSSIBLE);
1284 	}
1285 
1286 	status = lpsched_end_change(svc, dest, id);
1287 
1288 	if (status != PAPI_OK)
1289 		return (status);
1290 
1291 	return (PAPI_OK);
1292 }
1293