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

daemon.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/* By Elliot Lee <sopwith@redhat.com>. Ideas taken from autorun, by Harald Hoyer.
    Copyright (C) 1999 Red Hat Software.
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gnome.h>
#include <libgnomevfs/gnome-vfs.h>
#include <gconf/gconf-client.h>

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <mntent.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/iso_fs.h>
#include <linux/cdrom.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>

#include <gdk/gdkx.h>

#undef MD_DEBUG
#define MNTTYPE_ISO9660 "iso9660"
#define MNTOPT_USER "user"
#define MNTOPT_USERS "users"
#define MNTOPT_OWNER "owner"

#define CHECK_INTERVAL 2000
/* #define FLOPPY_SUPPORT 1 */ /* This keeps the drive light on constantly */

typedef struct _DevInfo DevInfo;
typedef struct _AppInfo AppInfo;

typedef enum {
        STATE_ACTIVE=0,
        STATE_INACTIVE=1,
        STATE_EMPTY=2,
        STATE_LAST=3
} DevState;

const char * const state_names[] = {
        "ACTIVE",
        "INACTIVE",
        "EMPTY"
};

typedef enum {
        DEV_CDROM,
        DEV_FLOPPY,
        DEV_OTHER
} DevType;

const char * const type_names[] = {
        "CDROM",
        "FLOPPY",
        "OTHER"
};

struct _DevInfo {
        DevType type;
        DevState state;
        int fd;

        char *fsname;
        char *fspath;
        char *unaliased_device;

        gboolean is_mounted;
        gboolean did_mount;
      gboolean supports_changed;
};

struct _AppInfo { 
        GList *devices;
        GHashTable *devs_by_fsname;

        GList *fstab_mountable_devices;
        time_t fstab_mtime;

        gboolean have_mtab_mtime;
        time_t mtab_mtime;

        int freeze_count;

      /* config */
      GConfClient *conf_client;
      gboolean do_automount;
      gboolean do_autorun;
      gchar *autorun_path;
      gboolean do_cd_play;
      gchar *cd_play_command;
      gboolean do_dvd_play;
      gchar *dvd_play_command;
      gboolean do_cd_burn;
      gchar *cd_burn_command;
};

static AppInfo *app_init                      (void);

static void     app_device_add                (AppInfo           *ai,
                                               struct mntent     *ent);
static void     app_device_remove             (AppInfo           *ai,
                                               DevInfo           *info);
static void     app_run                       (AppInfo           *ai);
static void     app_close                     (AppInfo           *ai);

static gint     app_devices_check_change      (AppInfo           *ai);
static gboolean app_check_devices             (AppInfo           *ai,
                                               gboolean           initial);
static void     app_devices_update_is_mounted (AppInfo           *ai);
static void     app_device_add_aliases        (AppInfo           *ai,
                                               const char        *alias,
                                               DevInfo           *dev);
static gboolean app_device_iso9660_add        (AppInfo           *ai,
                                               DevInfo           *dev);
static gboolean app_device_floppy_add         (AppInfo           *ai,
                                               DevInfo           *dev);

static void     app_device_set_state    (DevInfo *dev,
                                         AppInfo *ai);
static gboolean app_device_is_mounted   (DevInfo *dev);
static gboolean app_device_is_writer    (DevInfo *dev);
static void     app_device_mount        (DevInfo *dev,
                                         AppInfo *ai);
static void     app_device_run_cdplayer (DevInfo *dev,
                                         AppInfo *ai);
static void     app_device_run_cdburner (DevInfo *dev,
                                         AppInfo *ai);
static void     app_device_run_dvdplayer (DevInfo *dev,
                               AppInfo *ai);
static gboolean app_device_autorun      (DevInfo *dev,
                                         AppInfo *ai);
static void     app_device_ask_mixed    (DevInfo *dev,
                                         AppInfo *ai);


static gboolean mntent_has_option (const char *optlist, const char *option);
static gboolean mntent_is_removable_fs (struct mntent *ent);

struct mntent *mntent_copy    (const struct mntent *ent);
static int     mntent_compare (const struct mntent *enta,
                               const struct mntent *entb);
static void    mntent_free    (struct mntent       *ent);

static void     cdrom_ioctl_frenzy      (int      fd);

static gboolean verbose = FALSE;

 /* Like, access but a bit more accurate - access will let
  * root do anything. Does not get read-only or no-exec
  * filesystems right.
  */
static gboolean
my_g_check_permissions (gchar *filename,
                        int    mode)
{
        int euid = geteuid ();
        int egid = getegid ();

        struct stat statbuf;

        if (stat (filename, &statbuf) == 0) {
                if ((mode & R_OK) &&
                    !((statbuf.st_mode & S_IROTH) ||
                      ((statbuf.st_mode & S_IRUSR) && euid == statbuf.st_uid) ||
                      ((statbuf.st_mode & S_IRGRP) && egid == statbuf.st_gid)))
                        return FALSE;
                if ((mode & W_OK) &&
                    !((statbuf.st_mode & S_IWOTH) ||
                      ((statbuf.st_mode & S_IWUSR) && euid == statbuf.st_uid) ||
                      ((statbuf.st_mode & S_IWGRP) && egid == statbuf.st_gid)))
                        return FALSE;
                if ((mode & X_OK) &&
                    !((statbuf.st_mode & S_IXOTH) ||
                      ((statbuf.st_mode & S_IXUSR) && euid == statbuf.st_uid) ||
                      ((statbuf.st_mode & S_IXGRP) && egid == statbuf.st_gid)))
                        return FALSE;

                return TRUE;
        }
        return FALSE;
}

static void
magicdev_verbose (const char *format,
                  ...)
{
        if (verbose) {
                va_list vap;
                char *message;
                
                va_start (vap, format);
                message = g_strdup_vprintf (format, vap);
                va_end (vap);

                g_printerr ("magicdev: %s\n", message);
                g_free (message);
        }
}

static void
selection_get_func (GtkClipboard     *clipboard,
                    GtkSelectionData *selection_data,
                    guint             info,
                    gpointer          user_data_or_owner)
{
}

static void
selection_clear_func (GtkClipboard *clipboard,
                      gpointer      user_data_or_owner)
{
        return;
}

#define SELECTION_NAME "_MAGICDEV_SELECTION"

static gboolean
magicdev_get_lock (void)
{
        static const GtkTargetEntry targets[] = {
                { SELECTION_NAME, 0, 0 }
        };

        gboolean result = FALSE;

        GtkClipboard *clipboard;
        Atom clipboard_atom = gdk_x11_get_xatom_by_name (SELECTION_NAME);

        XGrabServer (GDK_DISPLAY());

        if (XGetSelectionOwner (GDK_DISPLAY(), clipboard_atom) != None)
                goto out;

        clipboard = gtk_clipboard_get (gdk_atom_intern (SELECTION_NAME, FALSE));

        if (!gtk_clipboard_set_with_data  (clipboard, targets, G_N_ELEMENTS (targets),
                                           selection_get_func, selection_clear_func, NULL))
                goto out;

        result = TRUE;

 out:
        XUngrabServer (GDK_DISPLAY());
        gdk_flush();
        
        return result;
}

static void
load_config (AppInfo *ai)
{
      ai->do_automount = gconf_client_get_bool (ai->conf_client,
                  "/apps/magicdev/do_automount", NULL);
      ai->do_cd_play = gconf_client_get_bool (ai->conf_client,
                  "/apps/magicdev/do_cd_play", NULL);
      ai->do_autorun = (gconf_client_get_bool (ai->conf_client,
                  "/apps/magicdev/do_autorun", NULL) && ai->do_automount);
      ai->do_dvd_play = gconf_client_get_bool (ai->conf_client,
                  "/apps/magicdev/do_dvd_play", NULL);
      ai->cd_play_command = gconf_client_get_string (ai->conf_client,
                  "/apps/magicdev/cd_play_command", NULL);
      ai->autorun_path = gconf_client_get_string (ai->conf_client,
                  "/apps/magicdev/autorun_path", NULL);
      ai->dvd_play_command = gconf_client_get_string (ai->conf_client,
                  "/apps/magicdev/dvd_play_command", NULL);
      ai->do_cd_burn = gconf_client_get_bool (ai->conf_client,
                  "/apps/magicdev/do_cd_burn", NULL);
      ai->cd_burn_command = gconf_client_get_string (ai->conf_client,
                  "/apps/magicdev/cd_burn_command", NULL);

        if (!(ai->do_automount || ai->do_autorun ||
              ai->do_cd_play || ai->do_cd_play))
                exit (0);
}

static void
config_changed_cb (GConfClient *client, guint id, GConfEntry *entry,
                    gpointer data)
{
      AppInfo *ai = (AppInfo *) data;

#ifdef MD_DEBUG
      g_message ("Caught config change");
#endif
      g_free (ai->cd_play_command);
      g_free (ai->autorun_path);
      g_free (ai->dvd_play_command);
      g_free (ai->cd_burn_command);
      load_config (ai);
}

static void
init_config (AppInfo *ai)
{
      ai->conf_client = gconf_client_get_default ();

      gconf_client_add_dir (ai->conf_client,
                  "/apps/magicdev",
                  GCONF_CLIENT_PRELOAD_ONELEVEL,
                  NULL);

      load_config (ai);

      gconf_client_notify_add (ai->conf_client,
                  "/apps/magicdev", config_changed_cb,
                  ai, NULL, NULL);
}

int main (int argc, char *argv[])
{
        AppInfo *ai;
        GnomeClient *client;

        static const struct poptOption popt_options [] = {
                { "verbose", '\0', POPT_ARG_NONE, &verbose, 0,
                  N_("Output verbose debugging information"), NULL },
            POPT_TABLEEND
        };

      bindtextdomain (PACKAGE, GNOMELOCALEDIR);
      bind_textdomain_codeset(PACKAGE, "UTF-8");
      textdomain (PACKAGE);

      gnome_program_init (PACKAGE, VERSION,
                            LIBGNOMEUI_MODULE,
                            argc, argv,
                            GNOME_PARAM_POPT_TABLE, popt_options,
                            GNOME_PARAM_APP_DATADIR, DATADIR, NULL);

      client = gnome_master_client ();
      if (magicdev_get_lock()) {
            gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
      } else {
            gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
            return 0;
      }

      gtk_signal_connect (GTK_OBJECT (client), "die",
                  GTK_SIGNAL_FUNC (gtk_main_quit), NULL);

        ai = app_init ();
      init_config (ai);

        /* Initialize list of devices that can be mounted by user
         */
        app_check_devices (ai, TRUE);

        app_run (ai);

        app_close (ai);

        return 0;
}

static AppInfo *
app_init (void)
{
        AppInfo *retval;

        retval = g_new0 (AppInfo, 1);

        retval->devs_by_fsname = g_hash_table_new (g_str_hash, g_str_equal);

        return retval;
}

static void
app_run (AppInfo *ai)
{
#ifdef MD_DEBUG
        g_message ("Git along little proggie");
#endif

        gtk_main ();
}

static void
app_close (AppInfo *ai)
{
        GList *ltmp;

        for (ltmp = ai->devices; ltmp; ltmp = g_list_next (ltmp)) {
                DevInfo *dev;

                dev = ltmp->data;

                if (dev->did_mount) {
                        char *argv[3];

                        argv[0] = "/bin/umount";
                        argv[1] = dev->fspath;
                        argv[2] = NULL;
                        gnome_execute_async (g_get_home_dir (), 2, argv);
                }
        }
}

/* We don't want DeviceInfo structures to disappear under us
 * while we are running a dialog, so we have app_freeze()
 * app_thaw() to suspend the periodic check
 */
static void
app_freeze (AppInfo *ai)
{
        ai->freeze_count++;
}

static void
app_thaw (AppInfo *ai)
{
        g_return_if_fail (ai->freeze_count > 0);
        
        ai->freeze_count--;
}

static int
list_compare (GList       *lista,
              GList       *listb,
              GCompareFunc compare)
{
        while (lista && listb) {
                int v = (*compare) (lista->data, listb->data);
                if (v)
                        return v;

                lista = lista->next;
                listb = listb->next;
        }

        if (lista != listb)
                return lista ? 1 : -1;

        return 0;
}

/* This function checks to see if the set of devices that can be mounted
  * by the user has changed.
  */
static gboolean
app_check_fstab_changes (AppInfo    *ai,
                         const char *fstab_path,
                         gboolean    initial)
{
        struct stat statbuf;
        struct mntent *ent;
        FILE *mef;
        GList *new_list = NULL;

        if (stat (fstab_path, &statbuf) == 0) {
                time_t new_time = MAX (statbuf.st_mtime, statbuf.st_ctime);
                
                if (!initial && ai->fstab_mtime == new_time)
                        return FALSE;

                ai->fstab_mtime = new_time;

                mef = setmntent (fstab_path, "r");

                if (mef) {
                        while ((ent = getmntent (mef)))
                                if (mntent_has_option (ent->mnt_opts, MNTOPT_USER) ||
                                    mntent_has_option (ent->mnt_opts, MNTOPT_USERS) ||
                                    mntent_has_option (ent->mnt_opts, MNTOPT_OWNER)) 
                                        new_list = g_list_prepend (new_list, mntent_copy (ent));
                        
                        new_list = g_list_sort (new_list, (GCompareFunc)mntent_compare);
                        endmntent (mef);
                }

                if (ai->fstab_mountable_devices &&
                    list_compare (ai->fstab_mountable_devices, new_list, (GCompareFunc)mntent_compare) == 0) {
                        g_list_foreach (new_list, (GFunc)mntent_free, NULL);
                        g_list_free (new_list);

                        return FALSE;
                }

                if (ai->fstab_mountable_devices) {
                        g_list_foreach (ai->fstab_mountable_devices, (GFunc)mntent_free, NULL);
                        g_list_free (ai->fstab_mountable_devices);
                }

                ai->fstab_mountable_devices = new_list;

                return TRUE;
        }

        return FALSE;
}

static gboolean
app_check_devices (AppInfo *ai,
                   gboolean initial)
{
        gboolean changed = app_check_fstab_changes (ai, _PATH_MNTTAB, initial);

#ifdef MD_DEBUG
        if (initial)
                g_message ("Building initial device list");
        else if (changed)
                g_message ("Found device list changes");
#endif        

        if (initial || changed) {
                GList *tmp_list;

                tmp_list = ai->devices;
                while (tmp_list) {
                        DevInfo *info = tmp_list->data;
                        tmp_list = tmp_list->next;

                        app_device_remove (ai, info);
                }

                tmp_list = ai->fstab_mountable_devices;
                while (tmp_list) {
                        struct mntent *ent = tmp_list->data;

                        if (mntent_is_removable_fs (ent))
                                app_device_add (ai, ent);

                        tmp_list = tmp_list->next;
                }

                app_devices_update_is_mounted (ai);
                g_list_foreach (ai->devices, (GFunc)app_device_set_state, ai);

#ifdef MD_DEBUG
                g_message ("Current user-mountable devices:");
                tmp_list = ai->fstab_mountable_devices;
                while (tmp_list) {
                        struct mntent *ent = tmp_list->data;

                        g_message ("   fsname=%s dir=%s", ent->mnt_fsname, ent->mnt_dir);

                        tmp_list = tmp_list->next;
                }
                g_message ("Current devices handled by magicdev:");
                tmp_list = ai->devices;
                while (tmp_list) {
                        DevInfo *info = tmp_list->data;

                        g_message ("   fsname=%s fspath=%s", info->fsname, info->fspath);
                        g_message ("      type=%s state=%s", type_names[info->type], state_names[info->state]);
                        g_message ("      is_mounted=%d did_mount=%d supports_changed=%d", info->is_mounted, info->did_mount, info->supports_changed);

                        tmp_list = tmp_list->next;
                }
#endif /* MD_DEBUG */
        }

        if (initial)
            gtk_timeout_add (CHECK_INTERVAL, (GtkFunction)app_devices_check_change, ai);

        return changed;
}

static gboolean
mntent_has_fs_type (struct mntent *ent,
                    const gchar   *fstype)
{
        char **fstypes = g_strsplit (ent->mnt_type, ",", -1);
        int i;
        gboolean result = TRUE;
        
        for (i = 0; fstypes[i]; i++) {
                if (strcmp (fstypes[i], fstype) == 0) {
                        result = TRUE;
                        break;
                }
        }

        g_strfreev (fstypes);

        return result;
}

static gboolean
mntent_is_removable_fs (struct mntent *ent)
{
        if (mntent_has_fs_type (ent, MNTTYPE_ISO9660))
                return TRUE;

#ifdef FLOPPY_SUPPORT
        if (!strncmp (ent->mnt_fsname, "/dev/fd", strlen ("/dev/fd")))
                return TRUE;
#endif

        return FALSE;
}

static gboolean
mntent_has_option (const char *optlist,
                   const char *option)
{
        gboolean retval = FALSE;
        char **options;
        int i;

        options = g_strsplit (optlist, ",", -1);

        for (i = 0; options[i]; i++) {
                if (!strcmp (options[i], option)) {
                        retval = TRUE;
                        break;
                }
        }

        g_strfreev (options);

        return retval;
}

struct mntent *
mntent_copy (const struct mntent *ent)
{
        struct mntent *result = g_new (struct mntent, 1);

        result->mnt_fsname = g_strdup (ent->mnt_fsname);
        result->mnt_dir = g_strdup (ent->mnt_dir);
        result->mnt_type = g_strdup (ent->mnt_type);
        result->mnt_opts = g_strdup (ent->mnt_opts);
        result->mnt_freq = ent->mnt_freq;
        result->mnt_passno = ent->mnt_passno;

        return result;
}

static int
mntent_compare (const struct mntent *enta,
                const struct mntent *entb)
{
        int v;

        v = strcmp (enta->mnt_fsname, entb->mnt_fsname);
        if (v) return v;
        v = strcmp (enta->mnt_dir, entb->mnt_dir);
        if (v) return v;
        v = strcmp (enta->mnt_type, entb->mnt_type);
        if (v) return v;
        v = strcmp (enta->mnt_opts, entb->mnt_opts);
        if (v) return v;

        if (enta->mnt_freq != entb->mnt_freq)
                return enta->mnt_freq < entb->mnt_freq ? -1 : 1;
        if (enta->mnt_passno != entb->mnt_passno)
                return enta->mnt_passno < entb->mnt_passno ? -1 : 1;

        return 0;
}

static void
mntent_free (struct mntent *ent)
{
        g_free (ent->mnt_fsname);
        g_free (ent->mnt_dir);
        g_free (ent->mnt_type);
        g_free (ent->mnt_opts);

        g_free (ent);
}

static const char * const blacklisted_models[] = {
        /* https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=73661 */
        "HL-DT-STCD-RW/DVD-ROM GCC-4240N"
};

static gboolean
app_device_is_blacklisted (DevInfo *info)
{
        gchar *basename;
        gchar *procname = NULL;
        gchar *model = NULL;
        gboolean blacklisted = FALSE;

        magicdev_verbose ("Checking for blacklisting of %s (%s)",
                          info->fsname,
                          info->unaliased_device);

        basename = g_path_get_basename (info->unaliased_device);
        if (basename[0] == 'h' && basename[1] == 'd') {
                procname = g_build_filename ("/proc/ide", basename, "model", NULL);
        }

        if (procname)
                g_file_get_contents (procname, &model, NULL, NULL);

        if (model) {
                int i;

                if (verbose) {
                        char *p = model + strlen (model);
                        while (p > model && g_ascii_isspace (p[-1])) {
                                p[-1] = '\0';
                                p--;
                        }
                        magicdev_verbose ("Model is %s (from %s)", model, procname);
                }

                for (i = 0; i < G_N_ELEMENTS(blacklisted_models); i++) {
                        if (strstr (model, blacklisted_models[i]) != NULL)
                                blacklisted = TRUE;
                }

                magicdev_verbose ("%s",
                                  blacklisted ? "Blacklisted" : "Not blacklisted");
                        
        } else {
                magicdev_verbose ("Could not retrieve model");
        }

        g_free (model);
        g_free (procname);

        return blacklisted;
}

static void
app_device_add (AppInfo       *ai,
                struct mntent *ent)
{
        DevInfo *newdev = NULL;
        gboolean ok;

        newdev = g_new0(DevInfo, 1);
        newdev->fd = -1;
        newdev->fsname = g_strdup (ent->mnt_fsname);
        newdev->fspath = g_strdup (ent->mnt_dir);

        if (mntent_has_fs_type (ent, MNTTYPE_ISO9660))
                ok = app_device_iso9660_add (ai, newdev);
        else if (!strncmp (ent->mnt_fsname, "/dev/fd", strlen ("/dev/fd")))
                ok = app_device_floppy_add (ai, newdev);
        else
                goto out; /* not handled */

        if (!ok)
                goto out;

        ai->devices = g_list_append (ai->devices, newdev);
        app_device_add_aliases (ai, g_strdup (newdev->fsname), newdev);
        if (app_device_is_blacklisted (newdev)) {
                app_device_remove (ai, newdev);
                return;
        }
        
#ifdef MD_DEBUG
        g_message ("Device %s came through (type %s)", newdev->fsname, type_names[newdev->type]);
#endif
        return;

 out:
        if (newdev) {
                close (newdev->fd);
                g_free (newdev->fsname);
                g_free (newdev->fspath);
                g_free (newdev->unaliased_device);
        }
        g_free (newdev);
}

static gboolean
remove_device_foreach (gpointer key,
                       gpointer value,
                       gpointer info)
{
        if (value == info) {
                g_free (key);
                return TRUE;
        } else
                return FALSE;
}

static void
app_device_remove (AppInfo *ai,
                   DevInfo *info)
{
        ai->devices = g_list_remove (ai->devices, info);

        g_hash_table_foreach_remove (ai->devs_by_fsname, (GHRFunc)remove_device_foreach, info);

        if (info->fd)
                close (info->fd);
        g_free (info->fsname);
        g_free (info->fspath);
        g_free (info->unaliased_device);

        g_free (info);
}

/* This is here because mtab lists devices by their symlink-followed
 * names rather than what is listed in fstab. *sigh*
 */
static void
app_device_add_aliases (AppInfo    *ai,
                        const char *alias,
                        DevInfo    *dev)
{
        char buf[PATH_MAX];
        int buflen;

        g_hash_table_insert (ai->devs_by_fsname, (gpointer)alias, dev);

        buflen = readlink (alias, buf, sizeof (buf));
        if (buflen < 1) {
                dev->unaliased_device = g_strdup (alias);
                return;
        }

        buf[buflen] = '\0';

        if (buf[0] != '/') {
                char buf2[PATH_MAX];
                char *dn;
                dn = g_path_get_dirname (alias);
                sprintf (buf2, "%s/%s", dn, buf);
                g_free (dn);
                strcpy (buf, buf2);
        }

        app_device_add_aliases (ai, g_strdup (buf), dev);
}

static gboolean
app_device_floppy_add (AppInfo *ai,
                       DevInfo *dev)
{
        if (my_g_check_permissions (dev->fsname, R_OK))
                return FALSE;

        dev->type = DEV_FLOPPY;

        return TRUE;
}

static void
cdrom_ioctl_frenzy (int fd)
{
        /* This is complete paranoia - if someone cleared CDO_USE_FFLAGS (meaning
         * "ignore O_NONBLOCK"), and CDO_CHECK_TYPE is set, then things will
         * be messed up.
          */
        ioctl (fd, CDROM_SET_OPTIONS, CDO_USE_FFLAGS);

        /* Stuff in here before that I've turned off. OWT - 7 Feb. 2001
          */
         /* Auto-close and auto-eject should work fine with magicdev, so leave them
          * at their defaults 
          */
#if 0 
        ioctl (fd, CDROM_CLEAR_OPTIONS, CDO_AUTO_CLOSE|CDO_AUTO_EJECT);
#endif      
        /* CDO_CHECK_TYPE breaks some crufy programs - it's cleared by default as
          * a backwards-compat thing. It doesn't really help us to set it.
          */
#if 0 
        ioctl (fd, CDROM_SET_OPTIONS, CDO_CHECK_TYPE);
#endif      

        /* We used to try and unlock the door here, but it could be considered
          * bad form. The problem is that if programs don't notice the disk change,
          * they can get very much confused. We could alternatively look for the media,
          * unmount, then remount, but (as long as the user is running gmc) having
          * having to right click on the icon and select eject isn't too bad.
          */
#if 0  
        ioctl (fd, CDROM_CLEAR_OPTIONS, CDO_LOCK);
#ifdef CDROM_LOCKDOOR
        ioctl (fd, CDROM_LOCKDOOR, 0);
#else
#warning "Need Linux kernel >= 2.2.4 to work with IDE."
#endif
#endif /* 0 */
}

static gboolean
is_video_dvd (DevInfo *dev)
{
      char *video_path;
      gboolean retval;

      if (dev->did_mount == FALSE)
            return FALSE;

      video_path = g_build_path (G_DIR_SEPARATOR_S, dev->fspath,
                  "VIDEO_TS", NULL);
      retval = g_file_test (video_path, G_FILE_TEST_IS_DIR);
      g_free (video_path);

      if (retval != FALSE)
            return retval;

      video_path = g_build_path (G_DIR_SEPARATOR_S, dev->fspath,
                  "video_ts", NULL);
      retval = g_file_test (video_path, G_FILE_TEST_IS_DIR);
      g_free (video_path);

      return retval;
}

static gboolean
app_device_iso9660_add (AppInfo *ai,
                        DevInfo *dev)
{
        dev->fd = open (dev->fsname, O_RDONLY|O_NONBLOCK|O_EXCL);
        if (dev->fd < 0)
                return FALSE;

        dev->type = DEV_CDROM;

        /* It's probably not a CD-ROM drive */
        if (ioctl (dev->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) < 0) {
            close (dev->fd); dev->fd = -1;
                return FALSE;
      }

        cdrom_ioctl_frenzy (dev->fd);

      /* does the drive support CDROM_MEDIA_CHANGED ? */
      if (ioctl (dev->fd, CDROM_MEDIA_CHANGED) < 0)
            dev->supports_changed = FALSE;
      else
            dev->supports_changed = TRUE;

        close (dev->fd); dev->fd = -1;

        return TRUE;
}

static void
app_device_floppy_set_state (DevInfo *dev,
                             AppInfo *ai)
{
        int fd, err;

        fd = open (dev->fsname, O_RDONLY);

        if (fd < 0) {
                err = errno;

                if (err == EBUSY)
                        dev->state = STATE_ACTIVE;
                else
                        dev->state = STATE_EMPTY;
        } else {
                close (fd);
                dev->state = STATE_INACTIVE;
        }
}

static void
app_device_cdrom_set_state (DevInfo *dev,
                            AppInfo *ai)
{
        if (dev->fd < 0) {
                dev->fd = open (dev->fsname, O_RDONLY|O_NONBLOCK|O_EXCL);
        }

      if (dev->fd < 0) {
            return;
      }

      if ((dev->supports_changed == TRUE) &&
                  (ioctl (dev->fd, CDROM_MEDIA_CHANGED) == 0))
            goto end;
#ifdef MD_DEBUG
      if (dev->supports_changed == TRUE)
            g_message ("Detected CD change with CDROM_MEDIA_CHANGED");
#endif
        if (ioctl (dev->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) == CDS_DISC_OK) {
                int disctype;

                disctype = ioctl (dev->fd, CDROM_DISC_STATUS, CDSL_CURRENT);
                switch (disctype) {
                case CDS_NO_INFO:
                case CDS_AUDIO:
                case CDS_DATA_1:
                case CDS_DATA_2:
                case CDS_XA_2_1:
                case CDS_XA_2_2:
                case CDS_MIXED:
                        dev->state = STATE_INACTIVE;
                        break;

                default:
                        dev->state = STATE_EMPTY;
                        break;
                }
        } else
                dev->state = STATE_EMPTY;

end:
        if (app_device_is_mounted (dev))
                dev->state = STATE_ACTIVE;
        
      close (dev->fd);
      dev->fd = -1;
}

static void
app_device_set_state (DevInfo *dev,
                      AppInfo *ai)
{
        switch (dev->type) {
        case DEV_CDROM:
                app_device_cdrom_set_state (dev, ai);
                break;
        case DEV_FLOPPY:
                app_device_floppy_set_state (dev, ai);
                break;
        default:
                break;
        }
}

static void
app_device_activate_cdrom (DevInfo *dev,
                           AppInfo *ai)
{
        int disctype;

        if (dev->fd < 0) {
                dev->fd = open (dev->fsname, O_RDONLY|O_NONBLOCK|O_EXCL);
      }

      if (dev->fd < 0) {
            return;
      }

        disctype = ioctl (dev->fd, CDROM_DISC_STATUS, CDSL_CURRENT);
        switch (disctype) {
        case CDS_AUDIO:
                app_device_run_cdplayer (dev, ai);
                break;

        case CDS_MIXED:
                app_device_ask_mixed (dev, ai);
                break;
                
        case CDS_DATA_1:
        case CDS_DATA_2:
        case CDS_XA_2_1:
        case CDS_XA_2_2:
            app_device_mount (dev, ai);
                break;

        case CDS_NO_INFO:
            if (app_device_is_writer (dev))
                  app_device_run_cdburner (dev, ai);
                break;
                
        default:
                /* Huh? */
                break;
        }

      close (dev->fd);
      dev->fd = -1;
}

static void
app_device_activate_floppy (DevInfo *dev,
                            AppInfo *ai)
{
        app_device_mount (dev, ai);
}

static gboolean
app_device_activate (DevInfo *dev,
                     AppInfo *ai)
{
#ifdef MD_DEBUG
      g_message ("app_device_activate, type: %d", dev->type);
#endif
        switch (dev->type) {
        case DEV_CDROM:
                app_device_activate_cdrom (dev, ai);
                break;
        case DEV_FLOPPY:
                app_device_activate_floppy (dev, ai);
                break;
        default:
                break;
        }

        return FALSE;
}

static gboolean
app_device_deactivate (DevInfo *dev,
                       AppInfo *ai)
{
        /* Nothing to do here... */
        dev->did_mount = FALSE;

        return TRUE;
}

typedef gboolean (*ChangeFunc) (DevInfo *dev, AppInfo *ai);

static void
app_device_check_change (DevInfo *dev,
                         AppInfo *ai)
{
        /* What functions to run for particular state transitions */
        static ChangeFunc state_transitions[STATE_LAST][STATE_LAST] = {
                /************  from: ACTIVE                 INACTIVE                 EMPTY */
                /* to */
                /* ACTIVE */   {     NULL,                  app_device_autorun,      app_device_autorun  },
                /* INACTIVE */ {     app_device_deactivate, NULL              ,      app_device_activate },
                /* EMPTY */    {     app_device_deactivate, NULL              ,      NULL                }
        };


        DevState old_state;

        if (ai->freeze_count != 0)
                return;

        old_state = dev->state;

        app_device_set_state (dev, ai);

        if (old_state != dev->state) {
                ChangeFunc func;

                func = state_transitions[dev->state][old_state];

#ifdef MD_DEBUG
                g_message ("State on %s changed from %s to %s, running %p",
                           dev->fsname, state_names[old_state], state_names[dev->state], func);
#endif
            if (func)
                  func (dev, ai);
#if 0
                if (func)
                        ai->needs_desktop_refresh |= func (dev, ai);
#endif
        }
}

static gint
app_devices_check_change (AppInfo *ai)
{
      app_devices_update_is_mounted (ai);

        g_list_foreach (ai->devices, (GFunc)app_device_check_change, ai);

        return TRUE;
}

static gboolean
app_device_is_mounted (DevInfo *dev)
{
        return dev->is_mounted;
}

static void
app_devices_update_is_mounted (AppInfo *ai)
{
        FILE *fh;
        char line[PATH_MAX * 3], mntpoint[PATH_MAX], devname[PATH_MAX];
        GList *ltmp;
        DevInfo *dev;
        struct stat statbuf;
        
        if (stat (_PATH_MOUNTED, &statbuf) == 0) {
                time_t new_time = MAX (statbuf.st_mtime, statbuf.st_ctime);

                if (ai->have_mtab_mtime && new_time == ai->mtab_mtime)
                        return;

                ai->have_mtab_mtime = TRUE;
                ai->mtab_mtime = new_time;
        } else
                return;
                
        for (ltmp = ai->devices; ltmp; ltmp = g_list_next (ltmp)) {
                dev = ltmp->data;

                dev->is_mounted = FALSE;
        }

        fh = fopen (_PATH_MOUNTED, "r");
        if (!fh)
                return;

        while (fgets (line, sizeof (line), fh)) {

                sscanf (line, "%s %s", devname, mntpoint);

                dev = g_hash_table_lookup (ai->devs_by_fsname, devname);
                if (dev)
                        dev->is_mounted = TRUE;
        }

        fclose (fh);
}

static void
app_device_run_command (DevInfo *dev, char *orig_command)
{
      char *argv[4];
      int i = 0;
      gchar *command;
      GString *exec_str = g_string_new (NULL);
      char *p, *q;

      command = g_strdup (orig_command);

      q = command;
      p = command;
      while ((p = strchr (p, '%')) != NULL) {
            if (*(p+1) == 'd') {
                  *p = '\0';
                  g_string_append (exec_str, q);
                  g_string_append (exec_str, dev->fsname);

                  q = p + 2;
                  p = p + 2;
            }
      }

      g_string_append (exec_str, q);

      argv[i++] = "/bin/sh";
      argv[i++] = "-c";
      argv[i++] = exec_str->str;
      argv[i] = NULL;

      gnome_execute_async (g_get_home_dir (), i, argv);

      g_string_free (exec_str, TRUE);
      g_free (command);
}

/* Do specific actions on various devices */
static void
app_device_run_dvdplayer (DevInfo *dev,
                      AppInfo *ai)
{
      if (ai->do_dvd_play) {
            app_device_run_command (dev, ai->dvd_play_command);
      }
}

static void
app_device_run_cdplayer (DevInfo *dev,
                         AppInfo *ai)
{
      if (ai->do_cd_play) {
            app_device_run_command (dev, ai->cd_play_command);
      }
}

static void
app_device_run_cdburner (DevInfo *dev,
                         AppInfo *ai)
{
      if (ai->do_cd_burn) {
            app_device_run_command (dev, ai->cd_burn_command);
      }
}

static void
app_device_mount (DevInfo *dev,
                  AppInfo *ai)
{
        char *argv[3];
        
        if (ai->do_automount == TRUE) {
                argv[0] = "/bin/mount";
                argv[1] = dev->fspath;
                argv[2] = NULL;
            gnome_execute_async (g_get_home_dir (), 2, argv);
                dev->did_mount = TRUE;
        }
}

static gboolean
run_me (char *path)
{
      GtkWidget *askme;
      gboolean retval;

      retval = TRUE;
      askme = gtk_message_dialog_new (NULL, 0,
                  GTK_MESSAGE_QUESTION,
                  GTK_BUTTONS_YES_NO,
                  _("Do you wish to run %s?"),
                  path);
      gtk_dialog_set_default_response (GTK_DIALOG (askme), GTK_RESPONSE_YES);

      switch (gtk_dialog_run (GTK_DIALOG (askme))) {
      case GTK_RESPONSE_YES:
            break;
      default:
            retval = FALSE;
            break;
      }
      gtk_widget_destroy (askme);

      return retval;
}

static gboolean
app_device_autorun (DevInfo *dev,
                    AppInfo *ai)
{
        char *check_path;
        char *argv[2];
        int pid = -1;
        gboolean do_quit = FALSE;

      if (is_video_dvd (dev) && ai->do_dvd_play) {
            app_device_run_dvdplayer (dev, ai);
      }

        if (ai->do_autorun == TRUE) {
                char **autorun_fns;
                int i;

                app_freeze (ai);

                autorun_fns = g_strsplit (ai->autorun_path, ":", -1);

                for (i = 0; autorun_fns[i] && pid < 0 && !do_quit; i++) {

                        check_path = g_strdup_printf ("%s/%s",
                              dev->fspath, autorun_fns[i]);
                  argv[0] = check_path;
                  argv[1] = NULL;

                        if (!my_g_check_permissions (check_path, X_OK))
                                continue;

                  if (run_me (check_path)) {
                        pid = gnome_execute_async (g_get_home_dir (), 1, argv);
                        do_quit = TRUE;
                  }

                  g_free (check_path);
                }

                app_thaw (ai);
        }

        return TRUE;
}

static void
app_device_ask_mixed (DevInfo *dev,
                      AppInfo *ai)
{
        enum { MOUNT, PLAY } action = -1;

        if (ai->do_automount && ai->do_cd_play) {
                GtkWidget *askme;

                app_freeze (ai);
        
                askme = gtk_message_dialog_new (NULL, 0,
                                                GTK_MESSAGE_QUESTION,
                                                GTK_BUTTONS_NONE,
                                                _("The CD you inserted has both audio tracks and files on it.\n"
                                                  "Would you like to play the audio or browse the files?"));
                
                gtk_dialog_add_buttons (GTK_DIALOG (askme),
                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                        _("_Browse"),     MOUNT,
                                        _("_Play"),       PLAY,
                                        NULL);
                
                action = gtk_dialog_run (GTK_DIALOG (askme));
                
                gtk_widget_destroy (askme);

                app_thaw (ai);
                
        } else if (ai->do_automount) {
                action = MOUNT;
        } else if (ai->do_cd_play) {
                action = PLAY;
        }
                
        switch (action) {
        case MOUNT:
                app_device_mount (dev, ai);
                break;
        case PLAY:
                app_device_run_cdplayer (dev, ai);
                break;
        default:
                break;
        }
}

#define BURNER (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | CDC_DVD_RAM)

static gboolean
app_device_is_writer (DevInfo *dev)
{
      int drivetype;
        
      drivetype = ioctl (dev->fd, CDROM_GET_CAPABILITY, CDSL_CURRENT);

        if (drivetype < 0)
                return FALSE;
        else
                return (drivetype & BURNER) != 0;
}


Generated by  Doxygen 1.6.0   Back to index