forked from pool/cups-backends
359 lines
8.9 KiB
C
359 lines
8.9 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/*
|
|
* Authors: Jeffrey Stedfast <fejj@novell.com>
|
|
* Authors: Klaus Singvogel <kssingvo@suse.de>
|
|
*
|
|
* Copyright 2005 Novell, Inc. (www.novell.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <cups/cups.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
|
|
#include <dbus/dbus.h>
|
|
#include <hal/libhal.h>
|
|
|
|
|
|
static LibHalContext *
|
|
get_hal_ctx (void)
|
|
{
|
|
DBusConnection *connection;
|
|
LibHalContext *hal_ctx;
|
|
DBusError error;
|
|
|
|
if (!(hal_ctx = libhal_ctx_new ())) {
|
|
fprintf (stderr, "ERROR: Unable to create HAL context\n");
|
|
return NULL;
|
|
}
|
|
|
|
dbus_error_init (&error);
|
|
if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
|
|
fprintf (stderr, "ERROR: Unable to connect to DBUS: %s\n", error.message);
|
|
libhal_ctx_free (hal_ctx);
|
|
dbus_error_free (&error);
|
|
return NULL;
|
|
}
|
|
|
|
libhal_ctx_set_dbus_connection (hal_ctx, connection);
|
|
|
|
if (!libhal_ctx_init (hal_ctx, &error)) {
|
|
fprintf (stderr, "ERROR: Unable to init HAL context: %s\n", error.message);
|
|
libhal_ctx_free (hal_ctx);
|
|
dbus_error_free (&error);
|
|
return NULL;
|
|
}
|
|
|
|
return hal_ctx;
|
|
}
|
|
|
|
|
|
/*
|
|
* 'list_devices()' - List all HAL printer devices.
|
|
*/
|
|
|
|
static void
|
|
list_devices (void)
|
|
{
|
|
LibHalContext *hal_ctx;
|
|
char **printers;
|
|
int i, n;
|
|
|
|
if (!(hal_ctx = get_hal_ctx ()))
|
|
return;
|
|
|
|
printers = libhal_find_device_by_capability (hal_ctx, "printer", &n, NULL);
|
|
|
|
if (n == 0)
|
|
printf("direct hal \"Unknown\" \"Hal printing backend\"\n");
|
|
|
|
for (i = 0; i < n; i++) {
|
|
char *vendor, *product, *description;
|
|
char make_model[1024];
|
|
|
|
/* We only want printers that have device nodes */
|
|
if (!libhal_device_property_exists (hal_ctx, printers[i], "printer.device", NULL))
|
|
continue;
|
|
|
|
vendor = libhal_device_get_property_string (hal_ctx, printers[i], "printer.vendor", NULL);
|
|
product = libhal_device_get_property_string (hal_ctx, printers[i], "printer.product", NULL);
|
|
description = libhal_device_get_property_string (hal_ctx, printers[i], "printer.description", NULL);
|
|
|
|
/* Try our hardest to get a good name */
|
|
if (product != NULL) {
|
|
if (vendor != NULL) {
|
|
snprintf (make_model, sizeof (make_model), "%s %s", vendor, product);
|
|
} else {
|
|
strncpy (make_model, product, sizeof (make_model) - 1);
|
|
make_model[sizeof (make_model) - 1] = '\0';
|
|
}
|
|
} else if (description != NULL) {
|
|
strncpy (make_model, description, sizeof (make_model) - 1);
|
|
make_model[sizeof (make_model) - 1] = '\0';
|
|
} else if (vendor != NULL) {
|
|
snprintf (make_model, sizeof (make_model), "%s printer", vendor);
|
|
} else {
|
|
strcpy (make_model, "Unknown");
|
|
}
|
|
|
|
printf ("direct hal://%s \"%s\" \"%s\"\n", printers[i], make_model,
|
|
description != NULL ? description : make_model);
|
|
|
|
libhal_free_string (vendor);
|
|
libhal_free_string (product);
|
|
libhal_free_string (description);
|
|
}
|
|
|
|
libhal_ctx_shutdown (hal_ctx, NULL);
|
|
libhal_ctx_free (hal_ctx);
|
|
}
|
|
|
|
/*
|
|
* 'get_device_file()' - Get a device file from a HAL device UDI
|
|
*/
|
|
static char *
|
|
get_device_file (const char *uri)
|
|
{
|
|
LibHalContext *hal_ctx;
|
|
const char *udi;
|
|
char *device;
|
|
|
|
if (strncmp (uri, "hal://", 6) != 0)
|
|
return NULL;
|
|
|
|
if (!(hal_ctx = get_hal_ctx ()))
|
|
return NULL;
|
|
|
|
udi = uri + 6;
|
|
|
|
device = libhal_device_get_property_string (hal_ctx, udi, "printer.device", NULL);
|
|
|
|
libhal_ctx_shutdown (hal_ctx, NULL);
|
|
libhal_ctx_free (hal_ctx);
|
|
|
|
return device;
|
|
}
|
|
|
|
/*
|
|
* 'main()' - Send a file to the specified HAL printer device.
|
|
*
|
|
* Usage:
|
|
*
|
|
* printer-uri job-id user title copies options [file]
|
|
*/
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
int fp; /* Print file */
|
|
int copies; /* Number of copies to print */
|
|
char *device; /* Device file to open */
|
|
int fd; /* Device file descriptor */
|
|
ssize_t nwritten; /* Number of bytes written */
|
|
size_t nbytes; /* Number of bytes read */
|
|
size_t tbytes; /* Total number of bytes written */
|
|
char buffer[8192]; /* Output buffer */
|
|
char *bufptr; /* Pointer into buffer */
|
|
struct termios opts; /* Parallel port options */
|
|
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
|
|
struct sigaction action; /* Actions for POSIX signals */
|
|
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
|
|
#if defined(__linux) && defined(LP_POUTPA)
|
|
unsigned char status; /* Port status (off-line, out-of-paper, etc.) */
|
|
#endif /* __linux */
|
|
|
|
|
|
/*
|
|
* Make sure status messages are not buffered...
|
|
*/
|
|
|
|
setbuf (stderr, NULL);
|
|
|
|
/*
|
|
* Ignore SIGPIPE signals...
|
|
*/
|
|
|
|
#ifdef HAVE_SIGSET
|
|
sigset (SIGPIPE, SIG_IGN);
|
|
#elif defined(HAVE_SIGACTION)
|
|
memset (&action, 0, sizeof (action));
|
|
action.sa_handler = SIG_IGN;
|
|
sigaction (SIGPIPE, &action, NULL);
|
|
#else
|
|
signal (SIGPIPE, SIG_IGN);
|
|
#endif /* HAVE_SIGSET */
|
|
|
|
/*
|
|
* Check command-line...
|
|
*/
|
|
|
|
if (argc == 1) {
|
|
list_devices ();
|
|
return 0;
|
|
} else if (argc < 6 || argc > 7) {
|
|
fputs ("Usage: URI job-id user title copies options [file]\n", stderr);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* If we have 7 arguments, print the file named on the command-line.
|
|
* Otherwise, send stdin instead...
|
|
*/
|
|
|
|
if (argc == 6) {
|
|
fp = 0;
|
|
copies = 1;
|
|
} else {
|
|
if ((fp = open (argv[6], O_RDONLY)) == -1) {
|
|
perror ("ERROR: unable to open print file");
|
|
return 1;
|
|
}
|
|
|
|
copies = atoi (argv[4]);
|
|
}
|
|
|
|
if (!(device = get_device_file (argv[0]))) {
|
|
fprintf (stderr, "ERROR: Unable to open HAL device \"%s\"\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
do {
|
|
if ((fd = open (device, O_RDWR | O_EXCL)) == -1) {
|
|
if (errno == EBUSY) {
|
|
fputs ("INFO: Device busy; will retry in 30 seconds...\n", stderr);
|
|
sleep (30);
|
|
} else if (errno == ENXIO || errno == EIO || errno == ENOENT) {
|
|
fputs ("INFO: Printer not connected; will retry in 30 seconds...\n", stderr);
|
|
sleep (30);
|
|
} else {
|
|
fprintf (stderr, "ERROR: Unable to open device \"%s\": %s\n",
|
|
argv[0], strerror (errno));
|
|
return 1;
|
|
}
|
|
}
|
|
} while (fd == -1);
|
|
|
|
libhal_free_string (device);
|
|
|
|
/*
|
|
* Set any options provided...
|
|
*/
|
|
|
|
tcgetattr (fd, &opts);
|
|
|
|
opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
|
|
|
|
/**** No options supported yet ****/
|
|
|
|
tcsetattr (fd, TCSANOW, &opts);
|
|
|
|
/*
|
|
* Now that we are "connected" to the port, ignore SIGTERM so that we
|
|
* can finish out any page data the driver sends (e.g. to eject the
|
|
* current page... Only ignore SIGTERM if we are printing data from
|
|
* stdin (otherwise you can't cancel raw jobs...)
|
|
*/
|
|
|
|
if (argc < 7) {
|
|
#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
|
|
sigset (SIGTERM, SIG_IGN);
|
|
#elif defined(HAVE_SIGACTION)
|
|
memset (&action, 0, sizeof (action));
|
|
|
|
sigemptyset (&action.sa_mask);
|
|
action.sa_handler = SIG_IGN;
|
|
sigaction (SIGTERM, &action, NULL);
|
|
#else
|
|
signal (SIGTERM, SIG_IGN);
|
|
#endif /* HAVE_SIGSET */
|
|
}
|
|
|
|
#if defined(__linux) && defined(LP_POUTPA)
|
|
if (ioctl (fd, LPGETSTATUS, &status) == 0) {
|
|
fprintf (stderr, "DEBUG: LPGETSTATUS returned a port status of %02X...\n", status);
|
|
|
|
if (!(status & LP_POUTPA))
|
|
fputs ("WARNING: Media tray empty!\n", stderr);
|
|
else if (!(status & LP_PERRORP))
|
|
fputs ("WARNING: Printer fault!\n", stderr);
|
|
else if (!(status & LP_PSELECD))
|
|
fputs ("WARNING: Printer off-line.\n", stderr);
|
|
}
|
|
#endif /* __linux && LP_POUTPA */
|
|
|
|
/*
|
|
* Finally, send the print file...
|
|
*/
|
|
|
|
while (copies > 0) {
|
|
copies--;
|
|
|
|
if (fp != 0) {
|
|
fputs ("PAGE: 1 1\n", stderr);
|
|
lseek (fp, 0, SEEK_SET);
|
|
}
|
|
|
|
tbytes = 0;
|
|
while ((nbytes = read (fp, buffer, sizeof (buffer))) > 0) {
|
|
/*
|
|
* Write the print data to the printer...
|
|
*/
|
|
|
|
tbytes += nbytes;
|
|
bufptr = buffer;
|
|
|
|
while (nbytes > 0) {
|
|
if ((nwritten = write (fd, bufptr, nbytes)) == -1)
|
|
if (errno == ENOTTY)
|
|
nwritten = write (fd, bufptr, nbytes);
|
|
|
|
if (nwritten == -1) {
|
|
perror ("ERROR: Unable to send print file to printer");
|
|
break;
|
|
}
|
|
|
|
nbytes -= nwritten;
|
|
bufptr += nwritten;
|
|
}
|
|
|
|
if (nwritten == -1)
|
|
break;
|
|
|
|
if (argc > 6)
|
|
fprintf (stderr, "INFO: Sending print file, %lu bytes...\n", (unsigned long) tbytes);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Close the socket connection and input file and return...
|
|
*/
|
|
|
|
close (fd);
|
|
if (fp != 0)
|
|
close (fp);
|
|
|
|
fputs ("INFO: Ready to print.\n", stderr);
|
|
|
|
return 0;
|
|
}
|