Logo Search packages:      
Sourcecode: hal-cups-utils version File versions  Download package

hal.c

/*
 *
 *   HAL backend for the Common UNIX Printing System (CUPS).
 *
 *   Copyright 1997-2003 by Easy Software Products, all rights reserved.
 *   Copyright (C) 2004 Novell, Inc.
 *   Copyright (C) 2007, 2008 Red Hat, Inc.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Easy Software Products and are protected by Federal
 *   copyright law.  Distribution and use rights are outlined in the file
 *   "LICENSE" which should have been included with this file.  If this
 *   file is missing or damaged please contact Easy Software Products
 *   at:
 *
 *       Attn: CUPS Licensing Information
 *       Easy Software Products
 *       44141 Airport View Drive, Suite 204
 *       Hollywood, Maryland 20636-3111 USA
 *
 *       Voice: (301) 373-9603
 *       EMail: cups-info@cups.org
 *         WWW: http://www.cups.org
 *
 *   This file is subject to the Apple OS-Developed Software exception.
 *
 * Contents:
 *
 *   main()         - Send a file to the specified HAL device.
 *   list_devices() - List all HAL printer devices.
 */

/*
 * Include necessary headers.
 */

#include <cups/backend.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#ifdef __linux
#include <sys/ioctl.h>
#include <linux/lp.h>
#endif /* __linux */

#include <libhal.h>

/*
 * 'hal_init()' - Initializes a HAL context
 */
LibHalContext *
hal_init(void)
{
      LibHalContext *hal_ctx;
      DBusError error;
      DBusConnection *dbus_conn;

      hal_ctx = libhal_ctx_new ();

      if (hal_ctx == NULL) {
            fprintf (stderr, "ERROR: Unable to access HAL daemon\n");
            return NULL;
      }

      dbus_error_init (&error);
      dbus_conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);

      if (dbus_error_is_set (&error)) {
            fprintf (stderr, "ERROR: Could not connect to system bus: %s\n", error.message);
            dbus_error_free (&error);
            libhal_ctx_free (hal_ctx);
            return NULL;
      }

      libhal_ctx_set_dbus_connection (hal_ctx, dbus_conn);

      if (!libhal_ctx_init (hal_ctx, &error)) {
            fprintf (stderr, "ERROR: Could initialize HAL daemon: %s\n", error.message);
            dbus_error_free (&error);
            libhal_ctx_free (hal_ctx);
            return NULL;
      }

      return hal_ctx;
}

/*
 * 'list_devices()' - List all HAL printer devices.
 */

void
list_devices(void)
{
      LibHalContext *hal_ctx;
      char **printer_list;
      int num_printers, i;
      DBusError error;

      hal_ctx = hal_init ();

      if (hal_ctx == NULL)
            return;

      printer_list = libhal_find_device_by_capability (hal_ctx, "printer",
                                             &num_printers, NULL);
      if (printer_list == NULL || num_printers == 0)
            printf("direct hal \"Unknown\" \"Hal printing backend\"\n"); 

      for (i = 0; i < num_printers; i++) {
            const char *vendor, *product, *description,
                  *commandset, *serial;
            char make_model[1024];
            char deviceid[4000], *dp;
            size_t dlen;

            /* We only want printers that have device nodes */
            if (!libhal_device_property_exists (hal_ctx, printer_list[i],
                                        "printer.device", NULL))
                  continue;

            vendor = libhal_device_get_property_string (hal_ctx,
                                              printer_list[i],
                                              "printer.vendor",
                                              NULL);

            product = libhal_device_get_property_string (hal_ctx,
                                               printer_list[i],
                                               "printer.product",
                                               NULL);

            description = libhal_device_get_property_string (hal_ctx,
                                                   printer_list[i],
                                                   "printer.description",
                                                 NULL);

            commandset = libhal_device_get_property_string (hal_ctx,
                                                printer_list[i],
                                                "printer.commandset",
                                                NULL);

            serial = libhal_device_get_property_string (hal_ctx,
                                              printer_list[i],
                                              "printer.serial",
                                              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));
                  }
            } else if (description != NULL) {
                  strncpy (make_model, description, sizeof (make_model));
            } else if (vendor != NULL) {
                  snprintf (make_model, sizeof (make_model),
                          "%s printer", vendor);
            } else {
                  strncpy (make_model, "Unknown", sizeof (make_model));
            }

            /* Reconstruct the Device ID. */
            deviceid[0] = '\0';
            dp = deviceid;
            dlen = sizeof (deviceid) - 1;
            if (vendor != NULL) {
                  int l = snprintf (dp, dlen, "MFG:%s;", vendor);
                  dlen -= l;
                  dp += l;
            }
            if (product != NULL) {
                  int l = snprintf (dp, dlen, "MDL:%s;", product);
                  dlen -= l;
                  dp += l;
            }
            if (commandset != NULL) {
                  int l = snprintf (dp, dlen, "CMD:%s;", commandset);
                  dlen -= l;
                  dp += l;
            }
            snprintf (dp, dlen, "CLS:PRINTER;");
            dlen -= 12;
            dp += 12;
            if (serial != NULL) {
                  int l = snprintf (dp, dlen, "SN:%s;", serial);
                  dlen -= l;
                  dp += l;
            }
            /* Replace double-quotes with single quotes. */
            dp = deviceid;
            while (*dp != '\0') {
                  if (*dp == '"')
                        *dp = '\'';
                  dp++;
            }

            printf ("direct hal://%s \"%s\" \"%s\" \"%s\"\n",
                  printer_list[i], make_model, make_model,
                  deviceid);
      }

      dbus_error_init (&error);
      libhal_ctx_shutdown (hal_ctx, &error);
      if (dbus_error_is_set (&error))
            dbus_error_free (&error);

      libhal_ctx_free (hal_ctx);
}

/*
 * 'get_device_file()' - Get a device file from a HAL device UDI
 */
const char *
get_device_file (const char *uri)
{
      LibHalContext *hal_ctx;
      char *udi, *options;
      const char *device_file;

      hal_ctx = hal_init ();

      if (hal_ctx == NULL)
            return NULL;

      if (strncmp (uri, "hal://", 6) != 0)
            return NULL;

      udi = strdup (uri + 6);
      if ((options = strchr (udi, '?')) != NULL)
        *options++ = '\0';

      device_file = libhal_device_get_property_string (hal_ctx, udi,
                                             "printer.device",
                                           NULL);

      free (udi);
      return device_file;
}

/*
 * 'main()' - Send a file to the specified HAL printer device.
 *
 * Usage:
 *
 *    printer-uri job-id user title copies options [file]
 */

int               /* O - Exit status */
main(int  argc,         /* I - Number of command-line arguments (6 or 7) */
     char *argv[])      /* I - Command-line arguments */
{
  int       fp;         /* Print file */
  int       copies;           /* Number of copies to print */
  const char    *device_file;   /* Device file to open */
  int       fd;         /* Device file descriptor */
  int       wbytes;           /* Number of bytes written */
  size_t    nbytes,           /* Number of bytes read */
            tbytes;           /* Total number of bytes written */
  char            buffer[8192],     /* Output buffer */
            *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 */
  int       offline = -1,
            paperout = -1;
  

 /*
  * 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 (CUPS_BACKEND_OK);
  }
  else if (argc < 6 || argc > 7)
  {
    fputs("Usage: URI job-id user title copies options [file]\n", stderr);
    return (CUPS_BACKEND_FAILED);
  }

 /*
  * 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
  {
   /*
    * Try to open the print file...
    */

    if ((fp = open(argv[6], O_RDONLY)) < 0)
    {
      perror("ERROR: unable to open print file");
      return (CUPS_BACKEND_FAILED);
    }

    copies = atoi(argv[4]);
  }

 /*
  * Open the port device...
  */

  fputs ("STATE: +connecting-to-device\n", stderr);
  do
  {
    device_file = get_device_file (cupsBackendDeviceURI (argv));

    if (device_file == NULL)
    {
        fputs("INFO: Printer not connected; will retry in 30 seconds...\n",
            stderr);
      sleep(30);
      fd = -1;
      continue;
    }

    if ((fd = open (device_file, O_RDWR | O_EXCL)) == -1)
    {
      if (getenv("CLASS") != NULL)
      {
      /*
        * If the CLASS environment variable is set, the job was submitted
      * to a class and not to a specific queue.  In this case, we want
      * to abort immediately so that the job can be requeued on the next
      * available printer in the class.
      */

      fputs ("INFO: Unable to contact printer, queuing on next printer "
             "in class...\n", stderr);
      sleep (5); /* avoid job requeuing too rapidly */
      return (CUPS_BACKEND_FAILED);
      }

      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 (CUPS_BACKEND_FAILED);
      }
    }
  }
  while (fd < 0);

  fputs ("STATE: -connecting-to-device\n", stderr);

 /*
  * Set any options provided...
  */

  tcgetattr(fd, &opts);

  opts.c_lflag &= ~(ICANON | ECHO | ISIG);      /* Raw mode */

  /**** No options supported yet ****/

  tcsetattr(fd, TCSANOW, &opts);

#ifdef __linux
  {
    /* Tell the driver to return from write() with errno==ENOSPC on
     * paper-out */
    unsigned int t = 1;
    ioctl (fd, LPABORT, &t);
  }
#endif /* __linux */

 /*
  * 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)
 /*
  * Show the printer status before we send the file; normally, we'd
  * do this while we write data to the printer, however at least some
  * Linux kernels have buggy USB drivers which don't like to be
  * queried while sending data to the printer...
  *
  * Also, we're using the 8255 constants instead of the ones that are
  * supposed to be used, as it appears that the USB driver also doesn't
  * follow standards...
  */

  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 ((wbytes = write(fd, bufptr, nbytes)) < 0)
      {
        if (errno == ENOSPC)
        {
          if (paperout != 1)
          {
            fputs ("STATE: +media-empty-error\n", stderr);
            fputs ("ERROR: Out of paper!\n", stderr);
            paperout = 1;
          }
        }
        else if (errno == ENXIO)
        {
          if (offline != 1)
          {
            fputs ("STATE: +offline-error\n", stderr);
            fputs ("INFO: Printer is currently off-line.\n", stderr);
            offline = 1;
          }
        }
        else if (errno != EAGAIN && errno != EINTR && errno != ENOTTY)
        {
          perror ("ERROR: Unable to write print data");
          wbytes = -1;
          break;
        }
      }
      else
      {
        if (paperout)
        {
          fputs ("STATE: -media-empty-error\n", stderr);
          paperout = 0;
        }

        if (offline)
        {
          fputs ("STATE: -offline-error\n", stderr);
          fputs ("INFO: Printer is now on-line.\n", stderr);
          offline = 0;
        }
      }

      nbytes -= wbytes;
      bufptr += wbytes;
      }

      if (wbytes < 0)
        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);

  return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
}
                                          
/*
 * End of "$Id: hal.c,v 1.8 2007/06/07 16:49:54 twaugh Exp $".
 */

Generated by  Doxygen 1.6.0   Back to index