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