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