/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 */

/* $Id: lpd-misc.c 155 2006-04-26 02:34:54Z ktou $ */

#define	__EXTENSIONS__	/* for strtok_r() */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <errno.h>
#include <wait.h>
#include <stropts.h>
#include <papi_impl.h>

#include <config-site.h>

char *
fdgets(char *buf, size_t len, int fd)
{
	char	tmp;
	int	count = 0;

	memset(buf, 0, len);
	while ((count < len) && (read(fd, &tmp, 1) > 0))
		if ((buf[count++] = tmp) == '\n') break;

	if (count != 0)
		return (buf);
	return (NULL);
}

char *
queue_name_from_uri(uri_t *uri)
{
	char *result = NULL;

	if ((uri != NULL) && (uri->path != NULL)) {
		char *ptr = strrchr(uri->path, '/');

		if (ptr == NULL)
			result = uri->path;
		else
			result = ++ptr;
	}

	return (result);
}

static int
recvfd(int sockfd)
{
	int fd = -1;
#if defined(sun) && defined(unix) && defined(I_RECVFD)
	struct strrecvfd recv_fd;

	memset(&recv_fd, 0, sizeof (recv_fd));
	if (ioctl(sockfd, I_RECVFD, &recv_fd) == 0)
		fd = recv_fd.fd;
#else
	struct iovec    iov[1];
	struct msghdr   msg;

#ifdef CMSG_DATA
	struct cmsghdr cmp[1];
	char buf[24];	/* send/recv 2 byte protocol */

	memset(buf, 0, sizeof (buf));

	iov[0].iov_base = buf;
	iov[0].iov_len = sizeof (buf);

	msg.msg_control = cmp;
	msg.msg_controllen = sizeof (struct cmsghdr) + sizeof (int);
#else
	iov[0].iov_base = NULL;
	iov[0].iov_len = 0;
	msg.msg_accrights = (caddr_t)&fd;
	msg.msg_accrights = sizeof (fd);
#endif
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = NULL;
	msg.msg_namelen = 0;

	if (recvmsg(sockfd, &msg, 0) < 0)
		fd = -1;
#ifdef CMSG_DATA
	else
		fd = * (int *)CMSG_DATA(cmp);
#endif
#endif
	return (fd);
}

int
lpd_open(service_t *svc, char type, char **args, int timeout)
{
	int ac, rc = -1, fds[2];
	pid_t pid;
	char *av[64], *tmp, buf[BUFSIZ];

	if ((svc == NULL) || (svc->uri == NULL))
		return (-1);

#ifndef SUID_LPD_PORT
#define	SUID_LPD_PORT "/usr/lib/print/lpd-port"
#endif

	av[0] = SUID_LPD_PORT;
	ac = 1;

	/* server */
	av[ac++] = "-H";
	av[ac++] = svc->uri->host;

	/* timeout */
	if (timeout > 0) {
		snprintf(buf, sizeof (buf), "%d", timeout);
		av[ac++] = "-t";
		av[ac++] = strdup(buf);
	}

	/* operation */
	snprintf(buf, sizeof (buf), "-%c", type);
	av[ac++] = buf;

	/* queue */
	if (svc->uri->path == NULL) {
		tmp = "";
	} else {
		if ((tmp = strrchr(svc->uri->path, '/')) == NULL)
			tmp = svc->uri->path;
		else
			tmp++;
	}
	av[ac++] = tmp;

	/* args */
	if (args != NULL)
		while ((*args != NULL) && (ac < 62))
			av[ac++] = *args++;

	av[ac++] = NULL;

#if defined(sun) && defined(unix) && defined(I_RECVFD)
	pipe(fds);
#else
	socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
#endif

	switch (pid = fork()) {
	case -1:	/* failed */
		break;
	case 0:	 /* child */
		dup2(fds[1], 1);
		execv(av[0], &av[0]);
		perror("exec");
		exit(1);
		break;
	default: {	/* parent */
		int err, status = 0;

		while ((waitpid(pid, &status, 0) < 0) && (errno == EINTR))
			;
		errno = WEXITSTATUS(status);

		if (errno == 0)
			rc = recvfd(fds[0]);

		err = errno;
		close(fds[0]);
		close(fds[1]);
		errno = err;
		}
	}

	return (rc);
}