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