/*
 * Copyright (c) 1998 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"@(#)adaptor.c	1.22	99/06/16 SMI"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/systeminfo.h>

#include <syslog.h>

#include <adaptor.h>
#include <print/ns.h>

#ifndef RTLD_GLOBAL	/* for OSF/1 */
#define	RTLD_GLOBAL	0
#endif

/*
 * This file contains the implementation of the API used by the front half
 * of the BSD Print protocol adaptor to select and glue in the back half of
 * the print protocol adaptor dynamically.  Most of the functions in this
 * file are hollow shells that pick a back end, load it, and call the "real"
 * function from the loaded object, with the arguments passed in.  The loaded
 * functions have the naming convention {paradigm}_{function}.
 * These functions are in NO WAY MT-Safe.  They are intended to be used by
 * a single threaded adaptor, that is/may be called via inetd concurrently.
 */


static void *paradigm_handle = NULL;
static char *paradigm_name = NULL;
static int   paradigm_version = -1;
static ns_printer_t *printer_object = NULL;
static char *primary_name = NULL;

static int
is_localhost(char *name)
{
	char buf[BUFSIZ];

	if (name == NULL)
		return (0);

	if (strcasecmp(name, "localhost") == 0)
		return (0);

	(void) sysinfo(SI_HOSTNAME, buf, sizeof (buf));

	return (strcasecmp(name, buf));
}

/*
 * A hack for performance.  This will look for a local LP based printer in
 * the most efficient manner.  If found, it will circumvent all future
 * adaptor resolution and load the default lpsched translation module.
 */
static int
lpsched_adaptor_available(const char *printer)
{
	char buf[BUFSIZ], buf2[BUFSIZ];

	syslog(LOG_DEBUG, "in.lpd:lpsched_adaptor_available: entry\n");

	(void) snprintf(buf, sizeof (buf), "/etc/lp/interfaces/%s", printer);
	(void) snprintf(buf2, sizeof (buf2), "/etc/lp/classes/%s", printer);
	syslog(LOG_DEBUG, "in.lpd:lpsched_adaptor_available: printer is %s\n",
		 printer);
	if ((access(buf, F_OK) < 0) && (access(buf2, F_OK) < 0))
		return (-1);

	syslog(LOG_DEBUG,
		 "in.lpd:lpsched_adaptor_available: printer %s local\n",
		 printer);

	paradigm_handle = dlopen("/usr/lib/print/bsd-adaptor/bsd_lpsched.so",
				RTLD_NOW|RTLD_GLOBAL);
	if (paradigm_handle == NULL)
		return (-1);

	paradigm_name = LPSCHED;
	return (0);
}

/*
 * adaptor_avaliable() takes in the name of a printer, looks it up in the
 * name service, and dynamically loads backend support for the print paradigm
 * the printer is defined in.  If the printer is undefined, UNDEFINED is
 * returned.  If the spooling paradigm is not defined, and the printer is
 * remote, a "cascade" capability is loaded.
 */
int
adaptor_available(const char *printer)
{
	char	*path,
		*dir,
		*tmp,
		*tmp_path;
	ns_bsd_addr_t *addr;

	/*
	 * for performance, check lpsched first and foremost.  This will
	 * hide any NS information if there is an LP configuration, valid
	 * or not.
	 */
	if (lpsched_adaptor_available(printer) == 0)
		return (0);

	errno = 0;
	if (((printer_object = ns_printer_get_name(printer,
						NS_SVC_ETC)) == NULL) &&
	    (endprinterentry() == 0) &&
	    ((printer_object = ns_printer_get_name(printer, NULL)) == NULL)) {
		errno = ENOENT;
		return (-1);
	}

	if ((addr = ns_get_value(NS_KEY_BSDADDR, printer_object)) != NULL)
		primary_name = addr->printer;

	if ((paradigm_name = ns_get_value_string(NS_KEY_ADAPTOR_NAME,
					printer_object)) == NULL) {
		if ((addr != NULL) && (is_localhost(addr->server) == 0))
			paradigm_name = LPSCHED;
		else
			paradigm_name = CASCADE;
	}

	if ((tmp = strrchr(paradigm_name, ',')) != NULL) {
		*tmp++ = NULL;
		paradigm_version = atoi(tmp);
	}

	if ((path = ns_get_value_string(NS_KEY_ADAPTOR_PATH, printer_object))
	    == NULL)
		path = ADAPTOR_PATH;

	tmp_path = strdup(path);
	for (dir = strtok(tmp_path, ":,"); dir != NULL;
	    dir = strtok(NULL, ":,")) {
		static char object[BUFSIZ];

		if (paradigm_version < 0)
			(void) snprintf(object, sizeof (object), "%s/bsd_%s.so",
				dir, paradigm_name);
		else
			(void) snprintf(object, sizeof (object),
				"%s/bsd_%s.so.%d", dir, paradigm_name,
				paradigm_version);
		if ((paradigm_handle = dlopen(object, RTLD_NOW|RTLD_GLOBAL))
		    != NULL)
			break;
#ifdef DEBUG
		syslog(LOG_DEBUG, "dlopen(%d): %s", object, dlerror());
#endif
	}
	free(tmp_path);

	return (paradigm_handle == NULL);
}


static void *
adaptor_function(const char *paradigm, const char *function)
{
	char name[128];
	void *fpt = NULL;

	if (paradigm_handle == NULL)
		return (NULL);

	(void) snprintf(name, sizeof (name), "%s_%s", paradigm, function);
	if ((fpt = dlsym(paradigm_handle, name)) == NULL)
		syslog(LOG_ERR, "could not locate function: %s()", name);

	return (fpt);
}


int
adaptor_spooler_available(const char *printer)
{
	static int (*fpt)() = NULL;

	if ((fpt != NULL) ||
	    ((fpt = (int (*)())adaptor_function(paradigm_name,
						"spooler_available")) != NULL))
		/*
		 *  in the case of cascading,
		 *  use the local name of the printer
		 */
		if (strcmp(paradigm_name, "cascade") == 0)
			return ((int)(fpt)(printer));
		else
			return ((int)(fpt)
				(primary_name ? primary_name : printer));

	    return (-1);
}


int
adaptor_spooler_accepting_jobs(const char *printer)
{
	static int (*fpt)() = NULL;

	if ((fpt != NULL) ||
	    ((fpt = (int (*)())adaptor_function(paradigm_name,
			"spooler_accepting_jobs")) != NULL))
		return ((int)(fpt)(primary_name ? primary_name : printer));

	    return (-1);
}


int
adaptor_client_access(const char *printer, const char *host)
{
	static int (*fpt)() = NULL;

	if ((fpt != NULL) ||
	    ((fpt = (int (*)())adaptor_function(paradigm_name,
						"client_access")) != NULL))
		return ((int)(fpt)((primary_name ? primary_name : printer),
					host));

	    return (-1);
}


int
adaptor_restart_printer(const char *printer)
{
	static int (*fpt)() = NULL;

	/* need a cast */
	if ((fpt != NULL) ||
	    ((fpt = (int (*)())adaptor_function(paradigm_name,
						"restart_printer")) != NULL))
		return ((int)(fpt)(primary_name ? primary_name : printer));

	    return (-1);
}


char *
adaptor_temp_dir(const char *printer, const char *host)
{
	static char *(*fpt)() = NULL;

	/* need a cast */
	if ((fpt != NULL) ||
	    ((fpt = (char *(*)())adaptor_function(paradigm_name,
					    "temp_dir")) != NULL))
		return ((char *)(fpt)((primary_name ? primary_name : printer),
					host));

	    return (NULL);
}


int
adaptor_submit_job(const char *printer, const char *host, char *cf,
		    char **df_list)
{
	static int (*fpt)() = NULL;

	/* need a cast */
	if ((fpt != NULL) ||
	    ((fpt = (int (*)())adaptor_function(paradigm_name,
						"submit_job")) != NULL))
		return ((int)(fpt)((primary_name ? primary_name : printer),
					host, cf, df_list));

	    return (-1);
}


int
adaptor_show_queue(const char *printer, FILE *ofp, const int type,
			char **list)
{
	static int (*fpt)() = NULL;

	/* need a cast */
	if ((fpt != NULL) ||
	    ((fpt = (int (*)())adaptor_function(paradigm_name,
						"show_queue")) != NULL))
		return ((int)(fpt)((primary_name ? primary_name : printer),
					ofp, type, list));

	    return (-1);
}


int
adaptor_cancel_job(const char *printer, FILE *ofp, const char *user,
			const char *host, char **list)
{
	static int (*fpt)() = NULL;

	/* need a cast */
	if ((fpt != NULL) ||
	    ((fpt = (int (*)())adaptor_function(paradigm_name,
						"cancel_job")) != NULL))
		return ((int)(fpt)((primary_name ? primary_name : printer),
					ofp, user, host, list));

	    return (-1);
}
