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/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28/*LINTLIBRARY*/
29
30#include <stdio.h>
31#include <stdarg.h>
32#include <libintl.h>
33#include <string.h>
34#include <stdlib.h>
35#include <errno.h>
36
37
38/* lpsched include files */
39#include "lp.h"
40#include "msgs.h"
41#include "printers.h"
42#include "class.h"
43
44#include <papi_impl.h>
45
46
47/*
48 * Format and send message to lpsched (die if any errors occur)
49 */
50/*VARARGS1*/
51int
52snd_msg(service_t *svc, int type, ...)
53{
54	int rc = -1;
55	va_list	ap;
56
57	if (svc == NULL)
58		return (-1);
59
60	/* fill the message buffer */
61	va_start(ap, type);
62	rc = _putmessage(svc->msgbuf, type, ap);
63	va_end(ap);
64	if (rc < 0) {
65		detailed_error(svc,
66		    gettext("unable to build message for scheduler: %s"),
67		    strerror(errno));
68		return (rc);
69	}
70
71	/* write the message */
72	while (((rc = mwrite(svc->md, svc->msgbuf)) < 0) && (errno == EINTR)) {
73	}
74
75	if (rc < 0)
76		detailed_error(svc,
77		    gettext("unable to send message to scheduler: %s"),
78		    strerror(errno));
79	return (rc);
80}
81
82/*
83 * Receive message from lpsched (die if any errors occur)
84 */
85int
86rcv_msg(service_t *svc, int type, ...)
87{
88	int rc = -1;
89
90	if (svc == NULL)
91		return (-1);
92
93	/* read the message */
94	while (((rc = mread(svc->md, svc->msgbuf, svc->msgbuf_size)) < 0) &&
95	    (errno == EINTR)) {
96	}
97
98	if (rc < 0)
99		detailed_error(svc,
100		    gettext("unable to read message from scheduler: %s"),
101		    strerror(errno));
102	else {
103		va_list ap;
104
105		va_start(ap, type);
106		rc = _getmessage(svc->msgbuf, type, ap);
107		va_end(ap);
108
109		if (rc < 0)
110			detailed_error(svc,
111			gettext("unable to parse message from scheduler: %s"),
112			    strerror(errno));
113	}
114
115	return (rc);
116}
117
118papi_status_t
119lpsched_status_to_papi_status(int status)
120{
121	switch (status) {
122	case MNOMEM:
123		return (PAPI_TEMPORARY_ERROR);
124	case MNOFILTER:
125		return (PAPI_DOCUMENT_FORMAT_ERROR);
126	case MNOOPEN:
127		return (PAPI_DOCUMENT_ACCESS_ERROR);
128	case MERRDEST:
129	case MDENYDEST:
130		return (PAPI_NOT_ACCEPTING);
131	case MNOMEDIA:
132		return (PAPI_PRINT_SUPPORT_FILE_NOT_FOUND);
133	case MDENYMEDIA:
134	case MNOPERM:
135		return (PAPI_NOT_AUTHORIZED);
136	case MUNKNOWN:
137	case MNODEST:
138	case MNOINFO:
139		return (PAPI_NOT_FOUND);
140	case MTRANSMITERR:
141		return (PAPI_SERVICE_UNAVAILABLE);
142	case M2LATE:
143		return (PAPI_GONE);
144	case MBUSY:
145		return (PAPI_PRINTER_BUSY);
146	case MOK:
147	case MOKMORE:
148		return (PAPI_OK);
149	}
150
151	return (PAPI_INTERNAL_ERROR);
152}
153
154char *
155lpsched_status_string(short status)
156{
157		switch (status) {
158	case MNOMEM:
159		return (gettext("lpsched: out of memory"));
160	case MNOFILTER:
161		return (gettext("No filter available to convert job"));
162	case MNOOPEN:
163		return (gettext("lpsched: could not open request"));
164	case MERRDEST:
165		return (gettext("queue disabled"));
166	case MDENYDEST:
167		return (gettext("destination denied request"));
168	case MNOMEDIA:
169		return (gettext("unknown form specified in job"));
170	case MDENYMEDIA:
171		return (gettext("access denied to form specified in job"));
172	case MUNKNOWN:
173		return (gettext("no such resource"));
174	case MNODEST:
175		return (gettext("unknown destination"));
176	case MNOPERM:
177		return (gettext("permission denied"));
178	case MNOINFO:
179		return (gettext("no information available"));
180	case MTRANSMITERR:
181		return (gettext("failure to communicate with lpsched"));
182	default: {
183		static char result[16];
184
185		snprintf(result, sizeof (result), gettext("status: %d"),
186		    status);
187		return (result);
188		}
189	}
190}
191
192papi_status_t
193lpsched_alloc_files(papi_service_t svc, int number, char **prefix)
194{
195	papi_status_t result = PAPI_OK;
196	short status = MOK;
197
198	if ((svc == NULL) || (prefix == NULL))
199		return (PAPI_BAD_ARGUMENT);
200
201	if ((snd_msg(svc, S_ALLOC_FILES, number) < 0) ||
202	    (rcv_msg(svc, R_ALLOC_FILES, &status, prefix) < 0))
203		status = MTRANSMITERR;
204
205	if (status != MOK) {
206		detailed_error(svc,
207		gettext("failed to allocate %d file(s) for request: %s"),
208		    number, lpsched_status_string(status));
209		result = lpsched_status_to_papi_status(status);
210	}
211
212	return (result);
213}
214
215papi_status_t
216lpsched_commit_job(papi_service_t svc, char *job, char **tmp)
217/* job is host/req-id */
218{
219	papi_status_t result = PAPI_OK;
220	short status = MOK;
221	long bits;
222
223	if ((svc == NULL) || (job == NULL) || (tmp == NULL))
224		return (PAPI_BAD_ARGUMENT);
225
226	if ((snd_msg(svc, S_PRINT_REQUEST, job) < 0) ||
227	    (rcv_msg(svc, R_PRINT_REQUEST, &status, tmp, &bits) < 0))
228		status = MTRANSMITERR;
229
230	if (status != MOK) {
231		detailed_error(svc, gettext("failed to commit job (%s): %s"),
232			job, lpsched_status_string(status));
233		result = lpsched_status_to_papi_status(status);
234	}
235
236	return (result);
237}
238
239papi_status_t
240lpsched_start_change(papi_service_t svc, char *printer, int32_t job_id,
241		char **tmp)
242{
243	papi_status_t result = PAPI_OK;
244	short status = MOK;
245	char req[BUFSIZ];
246	char *dest;
247
248	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
249		return (PAPI_BAD_ARGUMENT);
250
251	dest = printer_name_from_uri_id(printer, job_id);
252	snprintf(req, sizeof (req), "%s-%d", dest, job_id);
253	free(dest);
254
255	if ((snd_msg(svc, S_START_CHANGE_REQUEST, req) < 0) ||
256	    (rcv_msg(svc, R_START_CHANGE_REQUEST, &status, tmp) < 0))
257		status = MTRANSMITERR;
258
259	if (status != MOK) {
260		detailed_error(svc,
261		gettext("failed to initiate change for job (%s-%d): %s"),
262		    printer,
263		    job_id, lpsched_status_string(status));
264		result = lpsched_status_to_papi_status(status);
265	}
266
267	return (result);
268}
269
270papi_status_t
271lpsched_end_change(papi_service_t svc, char *printer, int32_t job_id)
272{
273	papi_status_t result = PAPI_OK;
274	short status = MOK;
275	long bits;
276	char req[BUFSIZ];
277	char *dest;
278
279	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
280		return (PAPI_BAD_ARGUMENT);
281
282	dest = printer_name_from_uri_id(printer, job_id);
283	snprintf(req, sizeof (req), "%s-%d", dest, job_id);
284	free(dest);
285
286	if ((snd_msg(svc, S_END_CHANGE_REQUEST, req) < 0) ||
287	    (rcv_msg(svc, R_END_CHANGE_REQUEST, &status, &bits) < 0))
288		status = MTRANSMITERR;
289
290	if (status != MOK) {
291		detailed_error(svc,
292		gettext("failed to commit change for job (%s-%d): %s"), printer,
293		    job_id, lpsched_status_string(status));
294		result = lpsched_status_to_papi_status(status);
295	}
296
297	return (result);
298}
299
300papi_status_t
301lpsched_accept_printer(papi_service_t svc, char *printer)
302{
303	papi_status_t result = PAPI_OK;
304	short	status = MOK;
305	char	*req_id;
306	char *dest;
307
308	if ((svc == NULL) || (printer == NULL))
309		return (PAPI_BAD_ARGUMENT);
310
311	dest = printer_name_from_uri_id(printer, -1);
312	if ((snd_msg(svc, S_ACCEPT_DEST, dest) < 0) ||
313	    (rcv_msg(svc, R_ACCEPT_DEST, &status, &req_id) < 0))
314		status = MTRANSMITERR;
315	free(dest);
316
317	if ((status != MOK) && (status != MERRDEST)) {
318		detailed_error(svc, "%s: %s", printer,
319		    lpsched_status_string(status));
320	}
321	result = lpsched_status_to_papi_status(status);
322
323	return (result);
324}
325
326papi_status_t
327lpsched_reject_printer(papi_service_t svc, char *printer, char *message)
328{
329	papi_status_t result = PAPI_OK;
330	short	 status = MOK;
331	char	*req_id;
332	char *dest;
333
334	if ((svc == NULL) || (printer == NULL))
335		return (PAPI_BAD_ARGUMENT);
336
337	if (message == NULL)
338		message = "stopped by user";
339
340	dest = printer_name_from_uri_id(printer, -1);
341	if ((snd_msg(svc, S_REJECT_DEST, dest, message, 0) < 0) ||
342	    (rcv_msg(svc, R_REJECT_DEST, &status, &req_id) < 0))
343		status = MTRANSMITERR;
344	free(dest);
345
346	if ((status != MOK) && (status != MERRDEST)) {
347		detailed_error(svc, "%s: %s", printer,
348		    lpsched_status_string(status));
349	}
350	result = lpsched_status_to_papi_status(status);
351
352	return (result);
353}
354
355papi_status_t
356lpsched_enable_printer(papi_service_t svc, char *printer)
357{
358	papi_status_t result = PAPI_OK;
359	short	 status = MOK;
360	char	*req_id;
361	char *dest;
362
363	if ((svc == NULL) || (printer == NULL))
364		return (PAPI_BAD_ARGUMENT);
365
366	dest = printer_name_from_uri_id(printer, -1);
367	if ((snd_msg(svc, S_ENABLE_DEST, dest) < 0) ||
368	    (rcv_msg(svc, R_ENABLE_DEST, &status, &req_id) < 0))
369		status = MTRANSMITERR;
370	free(dest);
371
372	if ((status != MOK) && (status != MERRDEST)) {
373		detailed_error(svc, "%s: %s", printer,
374		    lpsched_status_string(status));
375	}
376	result = lpsched_status_to_papi_status(status);
377
378	return (result);
379}
380
381papi_status_t
382lpsched_disable_printer(papi_service_t svc, char *printer, char *message)
383{
384	papi_status_t result = PAPI_OK;
385	short	 status = MOK;
386	char	*req_id;
387	char *dest;
388
389	if ((svc == NULL) || (printer == NULL))
390		return (PAPI_BAD_ARGUMENT);
391
392	if (message == NULL)
393		message = "stopped by user";
394
395	dest = printer_name_from_uri_id(printer, -1);
396	if ((snd_msg(svc, S_DISABLE_DEST, dest, message, 0) < 0) ||
397	    (rcv_msg(svc, R_DISABLE_DEST, &status, &req_id) < 0))
398		status = MTRANSMITERR;
399	free(dest);
400
401	if ((status != MOK) && (status != MERRDEST)) {
402		detailed_error(svc, "%s: %s", printer,
403		    lpsched_status_string(status));
404	}
405	result = lpsched_status_to_papi_status(status);
406
407	return (result);
408}
409
410papi_status_t
411lpsched_load_unload_dest(papi_service_t handle, char *dest, int type)
412{
413	service_t *svc = handle;
414	papi_status_t result;
415	short status = MOK;
416
417	/* tell the scheduler it's going */
418	if (snd_msg(svc, type, dest, "", "") < 0)
419		return (PAPI_SERVICE_UNAVAILABLE);
420
421	switch (type) {
422	case S_LOAD_PRINTER:
423		type = R_LOAD_PRINTER;
424		break;
425	case S_UNLOAD_PRINTER:
426		type = R_UNLOAD_PRINTER;
427		break;
428	case S_LOAD_CLASS:
429		type = R_LOAD_CLASS;
430		break;
431	case S_UNLOAD_CLASS:
432		type = R_UNLOAD_CLASS;
433	}
434
435	if (rcv_msg(svc, type, &status) < 0)
436		return (PAPI_SERVICE_UNAVAILABLE);
437
438	result = lpsched_status_to_papi_status(status);
439
440	return (result);
441}
442
443papi_status_t
444lpsched_remove_class(papi_service_t handle, char *dest)
445{
446	papi_status_t result;
447
448	/* tell the scheduler it's going */
449	result = lpsched_load_unload_dest(handle, dest, S_UNLOAD_CLASS);
450
451	if (result == PAPI_OK) {
452		/* remove the scheduler config files */
453		if (delclass(dest) == -1)
454			result = PAPI_SERVICE_UNAVAILABLE;
455	}
456
457	return (result);
458}
459
460static void
461remove_from_class(papi_service_t handle, char *dest, CLASS *cls)
462{
463	if (dellist(&cls->members, dest) == 0) {
464		if (cls->members != NULL) {
465			if (putclass(cls->name, cls) == 0)
466				(void) lpsched_load_unload_dest(handle,
467				    cls->name, S_LOAD_CLASS);
468		} else
469			(void) lpsched_remove_class(handle, cls->name);
470	}
471}
472
473papi_status_t
474lpsched_remove_printer(papi_service_t handle, char *dest)
475{
476
477	papi_status_t result;
478
479	/* tell the scheduler it's going */
480	result = lpsched_load_unload_dest(handle, dest, S_UNLOAD_PRINTER);
481
482	if (result == PAPI_OK) {
483		CLASS *cls;
484		char *dflt;
485
486		/* remove the scheduler config files */
487		if (delprinter(dest) == -1)
488			return (PAPI_SERVICE_UNAVAILABLE);
489
490		/* remove from any classes */
491		while ((cls = getclass(NAME_ALL)) != NULL) {
492			if (searchlist(dest, cls->members) != 0)
493				remove_from_class(handle, dest, cls);
494			freeclass(cls);
495		}
496
497		/* reset the default if it needs to be done */
498		if (((dflt = getdefault()) != NULL) &&
499		    (strcmp(dflt, dest) == 0))
500			putdefault(NAME_NONE);
501	}
502
503	return (result);
504}
505
506papi_status_t
507lpsched_add_modify_class(papi_service_t handle, char *dest,
508		papi_attribute_t **attributes)
509{
510	papi_status_t result;
511	void *iter = NULL;
512	char **members = NULL;
513	char *member;
514
515	/*
516	 * The only attribute that we can modify for a class is the set of
517	 * members.  Anything else will be ignored.
518	 */
519	for (result = papiAttributeListGetString(attributes, &iter,
520	    "member-names", &member);
521	    result == PAPI_OK;
522	    result = papiAttributeListGetString(attributes, &iter,
523	    NULL, &member))
524		addlist(&members, member);
525
526	if (members != NULL) {
527		/* modify the configuration file */
528		CLASS class;
529
530		memset(&class, 0, sizeof (class));
531		class.name = dest;
532		class.members = members;
533
534		if (putclass(dest, &class) == -1) {
535			if ((errno == EPERM) || (errno == EACCES))
536				result = PAPI_NOT_AUTHORIZED;
537			else
538				result = PAPI_NOT_POSSIBLE;
539		} else
540			result = PAPI_OK;
541
542		freelist(members);
543	} else
544		result = PAPI_ATTRIBUTES;
545
546	/* tell the scheduler about the changes */
547	if (result == PAPI_OK)
548		result = lpsched_load_unload_dest(handle, dest, S_LOAD_CLASS);
549
550	return (result);
551}
552
553papi_status_t
554lpsched_add_printer(papi_service_t handle, char *dest,
555		papi_attribute_t **attributes)
556{
557	PRINTER *p;
558	papi_status_t result = PAPI_TEMPORARY_ERROR;
559
560	if ((p = calloc(1, sizeof (*p))) != NULL) {
561		p->name = strdup(dest);
562		p->banner = BAN_ALWAYS;
563		p->interface = strdup("/usr/lib/lp/model/uri");
564		p->fault_alert.shcmd = strdup("mail");
565
566		attributes_to_printer(attributes, p);
567
568		if (putprinter(dest, p) == -1) {
569			if ((errno == EPERM) || (errno == EACCES))
570				result = PAPI_NOT_AUTHORIZED;
571			else
572				result = PAPI_NOT_POSSIBLE;
573		} else
574			result = PAPI_OK;
575
576		freeprinter(p);
577	}
578
579	/* tell the scheduler about the changes */
580	if (result == PAPI_OK)
581		result = lpsched_load_unload_dest(handle, dest, S_LOAD_PRINTER);
582
583	return (result);
584}
585
586papi_status_t
587lpsched_add_modify_printer(papi_service_t handle, char *dest,
588		papi_attribute_t **attributes, int type)
589{
590	PRINTER *p;
591	papi_status_t result;
592
593	if (type == 0) {
594		if ((p = calloc(1, sizeof (*p))) != NULL) {
595			p->name = strdup(dest);
596			p->banner = BAN_ALWAYS;
597			p->interface = strdup("/usr/lib/lp/model/uri");
598			p->fault_alert.shcmd = strdup("mail");
599		}
600	} else
601		p = getprinter(dest);
602
603	if (p != NULL) {
604		attributes_to_printer(attributes, p);
605
606		if (putprinter(dest, p) == -1) {
607			if ((errno == EPERM) || (errno == EACCES))
608				result = PAPI_NOT_AUTHORIZED;
609			else
610				result = PAPI_NOT_POSSIBLE;
611		} else
612			result = PAPI_OK;
613
614		freeprinter(p);
615	} else
616		result = PAPI_NOT_POSSIBLE;
617
618	/* tell the scheduler about the changes */
619	if (result == PAPI_OK)
620		result = lpsched_load_unload_dest(handle, dest, S_LOAD_PRINTER);
621
622	return (result);
623}
624