Logo Search packages:      
Sourcecode: eggcups version File versions  Download package

ec-cups-manager.c

/*
 *  Copyright (C) 2004 Red Hat, Inc.
 *  Written by:
 *    Matthias Clasen <mclasen@redhat.com>
 *    Colin Walters <walters@redhat.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 Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#define EC_SESSION_CUPS_OBJECT_PATH "/com/redhat/PrinterManager/SessionCups"

#include "config.h"
#include "ec-cups-manager.h"
#include "rb-debug.h"
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <ftw.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <libgnomecups/gnome-cups-printer.h>
#include <libgnomecups/gnome-cups-util.h>
#include <cups/http.h>
#include <glib.h>

G_DEFINE_TYPE(ECCupsManager, ec_cups_manager, G_TYPE_OBJECT)

static void ec_cups_manager_finalize (GObject *object);
static void ec_cups_manager_dispose (GObject *object);
static void ec_cups_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
static void ec_cups_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static GObject *ec_cups_manager_constructor (GType type, guint n_construct_properties,
                                GObjectConstructParam *construct_properties);
static void spawn_cups (ECCupsManager *mgr);
static void rm_rf (const char *filename);
static char *g_mkdtemp (char *tmpl);
static void create_passwd (ECCupsManager *mgr);
static char * generate_password (void);
static void cleanup_temp (ECCupsManager *mgr);
static void populate_server_root (ECCupsManager *mgr);
static void printer_added_cb (const char *name, gpointer user_data);
static void printer_removed_cb (const char *name, gpointer user_data);
static void write_printers_conf (ECCupsManager *mgr);
static void initialize_printer_list (ECCupsManager *mgr);
static char * get_listen_address (ECCupsManager *mgr);
static void session_cupsd_unregister_handler (DBusConnection *connection, void *data);
static DBusHandlerResult session_cupsd_message_handler (DBusConnection *connection, DBusMessage *message, void *user_data);


enum
{
      PROP_NONE,
      PROP_DBUS_CONNECTION,
      PROP_PASSWORD,
      PROP_PORT
};

00081 struct ECCupsManagerPrivate
{
      GPid cupsd_pid;
      guint spawn_count;
      guint port;
      char *password;

      DBusConnection *dbus;

      GHashTable *printer_uri_map;

      char *request_root;
      char *server_root;
      char *server_config;

      guint outstanding_unknown_printers;

      guint queued_printer_reload_id;

      guint new_printer_id;
      guint removed_printer_id;
      guint child_watch_id;

      gboolean disposed;
};

static DBusObjectPathVTable
session_cupsd_vtable = { &session_cupsd_unregister_handler,
                   &session_cupsd_message_handler,
                   NULL,
                   NULL,
                   NULL,
                   NULL };

static void
ec_cups_manager_class_init (ECCupsManagerClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS (klass);

      object_class->dispose = ec_cups_manager_dispose;
      object_class->finalize = ec_cups_manager_finalize;
      object_class->constructor = ec_cups_manager_constructor;
      object_class->set_property = ec_cups_manager_set_property;
      object_class->get_property = ec_cups_manager_get_property;

      g_object_class_install_property (object_class,
                               PROP_DBUS_CONNECTION,
                               g_param_spec_pointer ("dbus-connection", 
                                                 "DBusConnection", 
                                                 "Connection to session bus", 
                                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

      g_object_class_install_property (object_class,
                               PROP_PASSWORD,
                               g_param_spec_string ("password", 
                                                "password", 
                                                "Password", 
                                                "",
                                                G_PARAM_READABLE));
      g_object_class_install_property (object_class,
                               PROP_PORT,
                               g_param_spec_uint ("port", 
                                              "port", 
                                              "TCP port", 
                                              0, 32767,
                                              0,
                                              G_PARAM_READABLE));
}

static void
ec_cups_manager_init (ECCupsManager *mgr)
{
      mgr->priv = g_new0 (ECCupsManagerPrivate, 1);

      mgr->priv->printer_uri_map = g_hash_table_new_full (g_str_hash,
                                              g_str_equal,
                                              g_free,
                                              g_free);
}

static void
ec_cups_manager_dispose (GObject *object)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (object);

      if (mgr->priv->disposed)
            return;
      mgr->priv->disposed = TRUE;

      cleanup_temp (mgr);

      dbus_connection_unref (mgr->priv->dbus);

      if (mgr->priv->child_watch_id)
            g_source_remove (mgr->priv->child_watch_id);

      gnome_cups_printer_new_printer_notify_remove (mgr->priv->new_printer_id);
      gnome_cups_printer_printer_removed_notify_remove (mgr->priv->removed_printer_id);

      g_return_if_fail (mgr->priv != NULL);
}

static void
ec_cups_manager_finalize (GObject *object)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (object);

      g_return_if_fail (mgr->priv != NULL);

      rb_debug ("finalizing");

      g_hash_table_destroy (mgr->priv->printer_uri_map);

      g_free (mgr->priv->password);
      g_free (mgr->priv->request_root);
      g_free (mgr->priv->server_root);
      g_free (mgr->priv->server_config);

      g_free (mgr->priv);

      G_OBJECT_CLASS (ec_cups_manager_parent_class)->finalize (object);
}

static void
ec_cups_manager_set_property (GObject *object,
                        guint prop_id,
                        const GValue *value,
                        GParamSpec *pspec)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (object);

      switch (prop_id)
      {
      case PROP_DBUS_CONNECTION:
            mgr->priv->dbus = g_value_get_pointer (value);

            dbus_connection_ref (mgr->priv->dbus);
            if (dbus_connection_register_object_path (mgr->priv->dbus,
                                            EC_SESSION_CUPS_OBJECT_PATH,
                                            &session_cupsd_vtable,
                                            mgr) == FALSE) {
                  g_warning ("out of memory registering object path");
            } else
                  rb_debug ("registered session object");

            break;
      default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
      }
}

static void
ec_cups_manager_get_property (GObject *object,
                        guint prop_id,
                        GValue *value,
                        GParamSpec *pspec)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (object);

      switch (prop_id)
      {
      case PROP_DBUS_CONNECTION:
            g_value_set_pointer (value, mgr->priv->dbus);
            break;
      case PROP_PORT:
            g_value_set_uint (value, mgr->priv->port);
            break;
      case PROP_PASSWORD:
            g_value_set_string (value, mgr->priv->password);
            break;
      default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
      }
}

static GObject *
ec_cups_manager_constructor (GType type, guint n_construct_properties,
                    GObjectConstructParam *construct_properties)
{
      ECCupsManager *mgr;
      ECCupsManagerClass *klass;
      GObjectClass *parent_class;  

      klass = EC_CUPS_MANAGER_CLASS (g_type_class_peek (EC_TYPE_CUPS_MANAGER));

      parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
      mgr = EC_CUPS_MANAGER (parent_class->constructor (type, n_construct_properties,
                                            construct_properties));


      mgr->priv->new_printer_id = 
            gnome_cups_printer_new_printer_notify_add (printer_added_cb, mgr);
      mgr->priv->removed_printer_id = 
            gnome_cups_printer_printer_removed_notify_add (printer_removed_cb, mgr);
      
      mgr->priv->password = generate_password ();

      mgr->priv->request_root = g_build_filename (g_get_home_dir (),
                                        ".eggcups",
                                        "queue", NULL);
      if (!g_file_test (mgr->priv->request_root, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
            int tries = 0;
            while (mkdir (mgr->priv->request_root, 0750) < 0) {
                  g_critical ("Couldn't create queue directory: %s",
                            g_strerror (errno));
                  if (tries++ > 20)
                        exit (1);
                  g_usleep (10 * G_USEC_PER_SEC);
            }
      }

      mgr->priv->server_root = g_strdup_printf ("%s/%s-session-cups-server-root-XXXXXX",
                                      g_get_tmp_dir (), g_get_user_name ());
      
      mgr->priv->server_root = g_mkdtemp (mgr->priv->server_root);
      populate_server_root (mgr);
      create_passwd (mgr);
      initialize_printer_list (mgr);

      spawn_cups (mgr);

      return G_OBJECT (mgr);
}

ECCupsManager *
ec_cups_manager_new (DBusConnection *connection)
{
      return EC_CUPS_MANAGER (g_object_new (EC_TYPE_CUPS_MANAGER,
                                    "dbus-connection",
                                    connection,
                                    NULL));
}

static void
session_cupsd_unregister_handler (DBusConnection *connection, void *data)
{
      rb_debug ("unregistered");
}

static DBusHandlerResult
session_cupsd_message_handler (DBusConnection *connection,
                         DBusMessage *message,
                         void *user_data)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (user_data);

      rb_debug ("got message");

      if (dbus_message_is_method_call (message,
                               "com.redhat.PrinterManager",
                               "getPassword")) {
            DBusMessage *reply;
            char *password;

            rb_debug ("entering getPassword");

            reply = dbus_message_new_method_return (message);
            if (!reply) {
                  g_warning ("out of memory");
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }
            
            g_object_get (mgr, "password", &password, NULL);
            if (!dbus_message_append_args (reply, DBUS_TYPE_STRING,
                                     &password,
                                     DBUS_TYPE_INVALID)) {
                  g_warning ("couldn't append arguments to reply");
                  dbus_message_unref (reply);
                  g_free (password);
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }

            if (!dbus_connection_send (connection, reply, NULL)) {
                  g_warning ("couldn't send reply to getPassword");
                  dbus_message_unref (reply);
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }

            g_free (password);
            dbus_message_unref (reply);
            return DBUS_HANDLER_RESULT_HANDLED;
      } else if (dbus_message_is_method_call (message,
                                    "com.redhat.PrinterManager",
                                    "getPort")) {
            DBusMessage *reply;
            guint port;

            rb_debug ("entering getPort");

            reply = dbus_message_new_method_return (message);
            if (!reply) {
                  g_warning ("out of memory");
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }
            
            g_object_get (mgr, "port", &port, NULL);
            if (!dbus_message_append_args (reply, DBUS_TYPE_UINT32,
                                     port,
                                     DBUS_TYPE_INVALID)) {
                  g_warning ("couldn't append arguments to reply");
                  dbus_message_unref (reply);
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }

            if (!dbus_connection_send (connection, reply, NULL)) {
                  g_warning ("couldn't send reply to getPort");
                  dbus_message_unref (reply);
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }
            dbus_message_unref (reply);
            return DBUS_HANDLER_RESULT_HANDLED;
      }
      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

static char *
generate_password (void)
{
      guint count;
      GString *password = g_string_new ("");
      
      count = 0;
      while (count < 8) {
            char c = g_random_int_range (32, 127);
            if (!g_ascii_isprint (c))
                  continue;

            g_string_append_c (password, c);
            count++;
      }
      
      return g_string_free (password, FALSE);
}

static int
symlink_file_to_dir (const char *src_file, const char *target_dir)
{
      char *filename;
      char *target;
      int retval;

      filename = g_path_get_basename (src_file);
      target = g_build_filename (target_dir, filename, NULL);
      g_free (filename);

      if ((retval = symlink (src_file, target)) < 0) {
            g_critical ("Couldn't symlink %s to %s: %s",
                      src_file, target,
                      g_strerror (errno));
      }
      g_free (target);
      return retval;
}

static void
populate_server_root (ECCupsManager *mgr)
{
      GDir *d;
      GError *error = NULL;
      const char *system_cupsd_link_files[] = { "mime.convs", "mime.types",
                                      "pstoraster.convs" };
      const char *dirent;
      char *filename;
      int i;

      d = g_dir_open (SHARE_DIR "/server-root", 0,
                  &error);
      if (!d) {
            g_critical ("Couldn't open %s: %s",
                      SHARE_DIR "/server-root",
                      error->message);
            g_error_free (error);
            goto lose;
      }

      /* Copy over static but customized files */
      while ((dirent = g_dir_read_name (d)) != NULL) {
            filename = g_build_filename (SHARE_DIR "/server-root", dirent, NULL);
            symlink_file_to_dir (filename, mgr->priv->server_root);
            g_free (filename);
      }

      /* Link to any system cups files that don't need to be
       * modified.
       */
      for (i = 0; i < G_N_ELEMENTS (system_cupsd_link_files); i++) {
            filename = g_build_filename (EGGCUPS_CUPS_CONFIG_DIR,
                                   system_cupsd_link_files[i],
                                   NULL);
            symlink_file_to_dir (filename, mgr->priv->server_root);
            g_free (filename);
      }

      /* Create a certificates directory, just to keep cupsd happy. */
      filename = g_build_filename (mgr->priv->server_root, "certs", NULL);
      if (mkdir (filename, 0750) < 0
          && errno != EEXIST) {
        g_critical ("Couldn't create certificate directory %s: %s",
                  filename, g_strerror (errno));
      }
      g_free (filename);
      
lose:
      g_dir_close (d);
}

static void
create_passwd (ECCupsManager *mgr)
{
      char *filename;
      int fd;
      FILE *f;
      struct passwd *pw;
      struct group *grp;
      char md5[33];

      pw = getpwuid (getuid ());
      if (!pw) {
            g_critical ("Couldn't determine user information for %s!",
                      g_get_user_name ());
            return;
      }

      grp = getgrgid (pw->pw_gid);
      if (!grp) {
            g_critical ("Couldn't determine group information for group id %u!",
                      pw->pw_gid);
            return;
      }
      
      filename = g_build_filename (mgr->priv->server_root, "passwd.md5", NULL);
      if ((fd = open (filename, O_CREAT | O_WRONLY, 0600)) < 0) {
            g_critical ("Couldn't open %s: %s", filename,
                      g_strerror (errno));
            return;
      }

      f = fdopen (fd, "w");
      g_free (filename);
      if (fprintf (f, "%s:%s:%s\n", g_get_user_name (),
                 grp->gr_name,
                 httpMD5(g_get_user_name (),
                       "CUPS",
                       mgr->priv->password,
                       md5)) < 0) {
            g_critical ("Couldn't write to %s: %s",
                      filename, g_strerror (errno));
      }
      
      fclose (f);
}

static char *
get_listen_address (ECCupsManager *mgr)
{
      guint port;
      guint fail_count = 0;

      while (fail_count < 10) {
            struct sockaddr_in sin;
            int sock;
            socklen_t sinlen = sizeof (struct sockaddr_in);       

            port = g_random_int_range (20000, 32767);

            if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
                  g_critical ("Couldn't create socket: %s",
                            g_strerror (errno));
                  return NULL;
            }

            memset (&sin, 0, sinlen);
            sin.sin_family = AF_INET;
            sin.sin_addr.s_addr = g_htonl (INADDR_LOOPBACK);
            sin.sin_port = g_htonl (port);
            
            if (bind (sock, (struct sockaddr *) &sin, sinlen) >= 0) {
                  rb_debug ("bound to 127.0.0.1:%u", port);
                  shutdown (sock, 2);
                  mgr->priv->port = port;
                  return g_strdup_printf ("127.0.0.1:%u", port);
            }
            rb_debug ("failed to bind to 127.0.0.1:%u", port);
            fail_count++;
      }

      g_critical ("Couldn't find port to bind to!");
      return NULL;
}


/* This is stolen from http://bugzilla.gnome.org/show_bug.cgi?id=118563
 */
 /**
 * g_mkdtemp:
 * @tmpl: template dirname
 *
 * Creates a temporary folder. See the <function>mkdtemp()</function> 
 * documentation on glibc-based UNIX-like systems. This is a portability
 * wrapper, which simply calls <function>mkdtemp()</function> on systems
 *  that have it, and implements it in GLib otherwise.
 *
 * The parameter is a string that should match the rules for
 * <function>mkdtemp()</function>, i.e. end in "XXXXXX". The X string will 
 * be modified to form the name of a directory that didn't exist.
 *
 * Return value: The (as from <function>mkdtemp()</function>) name
 * of the created folder. In case of errors, NULL is returned.
 */
static char *
g_mkdtemp (char *tmpl)
{
#ifdef HAVE_MKDTEMP
  return mkdtemp (tmpl);
#else
  int len;
  char *XXXXXX;
  int count;
  static const char letters[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  static const int NLETTERS = sizeof (letters) - 1;
  glong value;
  GTimeVal tv;
  static int counter = 0;

  len = strlen (tmpl);
  if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
    return NULL;

  /* This is where the Xs start.  */
  XXXXXX = &tmpl[len - 6];

  /* Get some more or less random data.  */
  g_get_current_time (&tv);
  value = (tv.tv_usec ^ tv.tv_sec) + counter++;

  for (count = 0; count < 100; value += 7777, ++count)
    {
      glong v = value;

      /* Fill in the random bits.  */
      XXXXXX[0] = letters[v % NLETTERS];
      v /= NLETTERS;
      XXXXXX[1] = letters[v % NLETTERS];
      v /= NLETTERS;
      XXXXXX[2] = letters[v % NLETTERS];
      v /= NLETTERS;
      XXXXXX[3] = letters[v % NLETTERS];
      v /= NLETTERS;
      XXXXXX[4] = letters[v % NLETTERS];
      v /= NLETTERS;
      XXXXXX[5] = letters[v % NLETTERS];

      if (mkdir (tmpl, 0700)) {
        if (errno != EEXIST)
      /* Any other error will apply also to other names we might
       *  try, and there are 2^32 or so of them, so give up now.
       */
        return NULL;
      } else {
        return tmpl;
      }
    }

  /* We got out of the loop because we ran out of combinations to try.  */
  return NULL;
#endif
}

static char *
generate_cupsd_conf (ECCupsManager *mgr)
{
      char *filename = NULL;
      int out_fd;
      char *config_contents_str;
      char **config_contents;
      char **line;
      char *tmp_path;

      if (!g_file_get_contents (SHARE_DIR "/cupsd.conf",
                          &config_contents_str,
                          NULL,
                          NULL)) {
            g_warning ("Couldn't access %s", SHARE_DIR "/cupsd.conf");
            return NULL;
      }

      tmp_path = g_strdup_printf ("%s-session-cups-configuration-XXXXXX",
                            g_get_user_name ());
      out_fd = g_file_open_tmp (tmp_path,
                          &filename,
                          NULL);
      g_free (tmp_path);

      if (out_fd < 0)
            return NULL;
      rb_debug ("generating tmp cups config in %s", filename); 

      config_contents = g_strsplit (config_contents_str,
                              "\n",
                              0);
      g_free (config_contents_str);

      for (line = config_contents; *line; line++) {
            const char *variable_pos;
            const char *variable_end_pos;

            if ((variable_pos = strstr (*line, "@@"))
                && (variable_end_pos = strstr (variable_pos + 2, "@@"))) {
                  char *variable;
                  char *replacement = NULL;

                  variable = g_strndup (variable_pos + 2,
                                    variable_end_pos - (variable_pos + 2));
                  if (strcmp ("LISTEN_ADDRESS", variable) == 0) {
                        replacement = get_listen_address (mgr);
                        if (!replacement)
                              goto replacement_lose;
                  } else if (strcmp ("ACCESS_LOG", variable) == 0) {
                        replacement = g_strdup (g_getenv ("EGGCUPS_ACCESS_LOG"));
                        if (!replacement)
                              replacement = g_strdup ("/dev/null");
                  } else if (strcmp ("ERROR_LOG", variable) == 0) {
                        replacement = g_strdup (g_getenv ("EGGCUPS_ERROR_LOG"));
                        if (!replacement)
                              replacement = g_strdup ("/dev/null");
                  } else if (strcmp ("LOGLEVEL", variable) == 0) {
                        replacement = g_strdup (g_getenv ("EGGCUPS_LOGLEVEL"));
                        if (!replacement)
                              replacement = g_strdup ("None");
                  } else if (strcmp ("TMPDIR", variable) == 0) {
                        replacement = g_strdup (g_get_tmp_dir ());
                  } else if (strcmp ("REQUEST_ROOT", variable) == 0) {
                        replacement = g_strdup (mgr->priv->request_root);
                  } else if (strcmp ("SERVER_ROOT", variable) == 0) {
                        replacement = g_strdup (mgr->priv->server_root);
                  } else {
                        g_warning ("Unknown variable %s in %s",
                                 variable, SHARE_DIR "/cupsd.conf");
                        if (write (out_fd, *line, strlen (*line)) < 0)
                              goto replacement_lose;
                  }

                  if (write (out_fd, *line, variable_pos - *line) < 0)
                        goto replacement_lose;
                  if (write (out_fd, replacement, strlen (replacement)) < 0)
                        goto replacement_lose;
                  if (write (out_fd, variable_end_pos + 2, strlen (variable_end_pos + 2)) < 0)
                        goto replacement_lose;
                  if (write (out_fd, "\n", 1) < 0)
                        goto replacement_lose;

                  g_free (variable);
                  g_free (replacement);
                  continue;
            replacement_lose:
                  g_free (variable);
                  g_free (replacement);
                  goto lose;
            } else {
                  if (write (out_fd, *line, strlen (*line)) < 0)
                        goto lose;
                        
            }
            if (write (out_fd, "\n", 1) < 0)
                  goto lose;
      }
      
      close (out_fd);
      g_strfreev (config_contents);
      return filename;
lose:
      close (out_fd);
      g_strfreev (config_contents);
      unlink (filename);
      g_free (filename);
      return NULL;
}

static void
watch_cb (GPid pid, gint status, gpointer data)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (data);

      rb_debug ("got child death, trying respawn");
      if (WIFEXITED (status))
                g_warning ("cupsd exited with error code %d", WEXITSTATUS (status));
      else if (WIFSTOPPED (status)) 
                g_warning ("cupsd stopped unexpectedly with signal %d", WSTOPSIG (status));
      else if (WIFSIGNALED (status))
                g_warning ("cupsd died with signal %d", WTERMSIG (status));
      else
            g_warning ("cupsd died from an unknown cause");

      if (mgr->priv->queued_printer_reload_id > 0)
            g_source_remove (mgr->priv->queued_printer_reload_id);
      
      cleanup_temp (mgr);
      spawn_cups (mgr);
}

static void
spawn_cups (ECCupsManager *mgr)
{
      GPid pid;
      char *config_file;
      const char *cupsd_binary;
      GPtrArray *cupsd_argv;
      GError *error = NULL;

      mgr->priv->cupsd_pid = 0;

      mgr->priv->spawn_count++;
      if (mgr->priv->spawn_count > 5) {
            g_warning ("cupsd crashed more than 5 times, refusing to try again");
            return;
      }

      cupsd_argv = g_ptr_array_new ();
      cupsd_binary = g_getenv ("EGGCUPS_CUPS_BINARY_PATH") ?
        g_getenv ("EGGCUPS_CUPS_BINARY_PATH") : EGGCUPS_CUPS_BINARY_PATH;
      g_ptr_array_add (cupsd_argv, (char *) cupsd_binary);
      g_ptr_array_add (cupsd_argv, "-f");
      g_ptr_array_add (cupsd_argv, "-c");

      config_file = generate_cupsd_conf (mgr);
      g_free (mgr->priv->server_config);
      mgr->priv->server_config = g_strdup (config_file);

      g_ptr_array_add (cupsd_argv, config_file);
      g_ptr_array_add (cupsd_argv, NULL);

      if (!config_file) {
            g_warning ("couldn't generate cupsd config file");
            g_ptr_array_free (cupsd_argv, TRUE);
            return;
      }

      rb_debug ("spawning cupsd: %s", cupsd_binary);
      if (!g_spawn_async (NULL, (char **) cupsd_argv->pdata, NULL,
                      G_SPAWN_LEAVE_DESCRIPTORS_OPEN |
                      G_SPAWN_DO_NOT_REAP_CHILD,
                      NULL, NULL, &pid,
                      &error)) {
            g_warning ("Couldn't spawn cupsd: %s",
                     error->message);
            g_error_free (error);
            g_ptr_array_free (cupsd_argv, TRUE);
            unlink (config_file);
            g_free (config_file);
            return;
      }
      g_ptr_array_free (cupsd_argv, TRUE);
      g_free (config_file);
      mgr->priv->cupsd_pid = pid;
      rb_debug ("cups PID is %u", pid);
      if (mgr->priv->child_watch_id)
            g_source_remove (mgr->priv->child_watch_id);
      mgr->priv->child_watch_id = g_child_watch_add (pid, watch_cb, mgr);
}

static void
write_printer_data (ECCupsManager *mgr, FILE *f, const char *name)
{
      const char *printer_uri;
      
      printer_uri = g_hash_table_lookup (mgr->priv->printer_uri_map,
                                 name);
      if (!printer_uri)
            return;
      
      fprintf (f, "<Printer %s>\n", name);
      fprintf (f, "Info Created by eggcups\n");
      fprintf (f, "DeviceURI %s\n", printer_uri);
      fprintf (f, "</Printer>\n");
}

static gboolean
signal_cupsd_reload (gpointer data)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (data);

      if (!mgr->priv->cupsd_pid) {
            rb_debug ("no cups daemon running, not reloading");
            return FALSE;
      }

      rb_debug ("doing cupsd reload");
      write_printers_conf (mgr);

      /* Tell cupsd to reload its configuration. */
      rb_debug ("sending SIGHUP %u", mgr->priv->cupsd_pid);
      if (kill (mgr->priv->cupsd_pid, SIGHUP) < 0) {
            g_critical ("Couldn't SIGHUP %u", mgr->priv->cupsd_pid);
      }

      return FALSE;
}

static void
queue_printer_conf_reload (ECCupsManager *mgr)
{
      if (mgr->priv->queued_printer_reload_id > 0)
            g_source_remove (mgr->priv->queued_printer_reload_id);
      mgr->priv->queued_printer_reload_id = 
            g_timeout_add (5000, signal_cupsd_reload, mgr);
}

static void
printer_attributes_changed_cb (GnomeCupsPrinter *printer,
                         gpointer data)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (data);
      const char *name;
      const char *printer_uri;

      name = gnome_cups_printer_get_name (printer);
      printer_uri = gnome_cups_printer_get_uri (printer);

      rb_debug ("attributes for printer %s received", name);
      if (printer_uri == NULL) {
            rb_debug ("printer %s is local", name);
            g_object_unref (printer);
            return;
      }

      g_hash_table_insert (mgr->priv->printer_uri_map,
                       g_strdup (name),
                       g_strdup (printer_uri));

      queue_printer_conf_reload (mgr);
      
      g_object_unref (printer);
}

static void
initialize_printer_list (ECCupsManager *mgr)
{
      GList *printers;
      GList *printer;

      rb_debug ("initializing printer list");
      printers = gnome_cups_get_printers ();
      for (printer = printers; printer; printer = printer->next) {
            printer_added_cb (printer->data, mgr);
            g_free (printer->data);
      }
      g_list_free (printers);
}

static void
printer_added_cb (const char *name, gpointer user_data)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (user_data);
      GnomeCupsPrinter *printer;

      rb_debug ("printer %s added", name);

      printer = gnome_cups_printer_get (name);
      if (!gnome_cups_printer_get_attributes_initialized (printer)) {
            mgr->priv->outstanding_unknown_printers++;
            g_signal_connect_object (printer,
                               "attributes-changed",
                               G_CALLBACK (printer_attributes_changed_cb),
                               mgr, 0);
      } else {
            printer_attributes_changed_cb (printer, mgr);
      }
}

00953 struct ECWritePrinterData
{
      ECCupsManager *mgr;
      FILE *f;
};

static void
write_printer_data_from_hash (gpointer key, gpointer value,
                        gpointer user_data)
{
      struct ECWritePrinterData *data = user_data;
      const char *name = key;

      write_printer_data (data->mgr, data->f, name);
}

static void
write_printers_conf (ECCupsManager *mgr)
{
      char *filename;
      char *tmp_filename;
      struct ECWritePrinterData *data;

      data = g_new0 (struct ECWritePrinterData, 1);
      data->mgr = mgr;

      rb_debug ("writing new printers.conf");
      filename = g_build_filename (mgr->priv->server_root, "printers.conf", NULL);
      tmp_filename = g_build_filename (mgr->priv->server_root, "printers.conf.tmp", NULL);

      data->f = fopen (tmp_filename, "w");
      if (!data->f) {
            g_critical ("Couldn't open %s: %s", tmp_filename,
                      g_strerror (errno));
            goto lose_fopen;
      }
      g_hash_table_foreach (mgr->priv->printer_uri_map,
                        write_printer_data_from_hash,
                        data);
      fclose (data->f);
      g_free (data);
      rename (tmp_filename, filename);
lose_fopen:
      g_free (filename);
      g_free (tmp_filename);
}

static void
printer_removed_cb (const char *name, gpointer user_data)
{
      ECCupsManager *mgr = EC_CUPS_MANAGER (user_data);

      rb_debug ("printer %s removed", name);
      g_assert (g_hash_table_remove (mgr->priv->printer_uri_map, name));

      queue_printer_conf_reload (mgr);
}

static int
rm_rf_ftw_func (const char *name,
            const struct stat *buf,
            int flag,
            struct FTW *ftw)
{
  switch (flag) {
  case FTW_F:
        unlink (name);
        break;
  case FTW_DP:
        rmdir (name);
        break;
  case FTW_SL:
  case FTW_SLN:
        unlink (name);
        break;
  default:
        break;
  }
  return 0;
}

static void
rm_rf (const char *filename)
{
      nftw (filename, rm_rf_ftw_func,
            100, FTW_PHYS | FTW_DEPTH);
}

static void
cleanup_temp (ECCupsManager *mgr)
{
      struct stat buf;

      if (mgr->priv->server_root
          && stat (mgr->priv->server_root, &buf) == 0
          && buf.st_uid == getuid ()) {
            rm_rf (mgr->priv->server_root);
      }
      if (mgr->priv->server_config
          && stat (mgr->priv->server_config, &buf) == 0
          && buf.st_uid == getuid ()) {
            rm_rf (mgr->priv->server_config);
      }
}

Generated by  Doxygen 1.6.0   Back to index