Subject: Re: eject program
To: None <tech-kern@NetBSD.ORG>
From: Matthieu Herrb <matthieu@laas.fr>
List: tech-kern
Date: 10/11/1995 09:02:12
I have enhanced my old eject program. Now it can take nicknames as
device arguments or full paths to /dev nodes.

It will also try to umount any mounted partition before ejecting the
disk. It's not intended to be run setuid, so users won't eject disks
they can't umount.

However a man page is missing and it surely needs some testing.

Also, I haven't rebuilt it recently.

I'm working on a more general framework for removable devices
with user allocation, configurable automatic recognition and more. But
it does not progress very fast...

					Matthieu

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1995-10-11 08:49 MET by <matthieu@elwood>.
# Source directory was `/home/matthieu/netbsd/eject'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#    171 -rw-r--r-- Makefile
#   8234 -rw-r--r-- eject.c
#
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  echo 'WARNING: not restoring timestamps.  Consider getting and'
  echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
# ============= Makefile ==============
if test -f 'Makefile' && test X"$1" != X"-c"; then
  echo 'x - skipping Makefile (file already exists)'
else
  echo 'x - extracting Makefile (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
# @(#) $Id: Makefile,v 1.2 1994/12/22 11:40:02 cgd Exp $
X
PROG=	eject
SRCS=	eject.c
CFLAGS = -g -Wall
X
BINDIR = /usr/local/bin
NOMAN=
MAN=	eject.1
X
X.include <bsd.prog.mk>
SHAR_EOF
  $shar_touch -am 0722183995 'Makefile' &&
  chmod 0644 'Makefile' ||
  echo 'restore of Makefile failed'
  shar_count="`wc -c < 'Makefile'`"
  test 171 -eq "$shar_count" ||
    echo "Makefile: original size 171, current size $shar_count"
fi
# ============= eject.c ==============
if test -f 'eject.c' && test X"$1" != X"-c"; then
  echo 'x - skipping eject.c (file already exists)'
else
  echo 'x - extracting eject.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'eject.c' &&
/*
X * Copyright (c) 1995
X *	Matthieu Herrb.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed for the NetBSD Project
X *	by Matthieu Herrb.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
X * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
X * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
X * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
X * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X */
X
/*
X * Eject command
X *
X * It knows to eject floppies, CD-ROMS and tapes
X * and tries to unmount file systems first
X */
X
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/cdio.h>
#include <sys/mtio.h>
#ifdef __sparc
#include <sys/device.h>
#include "/sys/arch/sparc/dev/fdreg.h"
#include "/sys/arch/sparc/dev/fdvar.h"
#endif
#include <sys/ucred.h>
#include <sys/mount.h>
#include <sys/cdefs.h>
X
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include <string.h>
X
typedef struct DEVTAB {
X    char *name;
X    char *device;
X    u_int type;
} DEVTAB;
X
/*
X * known device nicknames and types
X * (used for selecting the proper ioctl to eject them)
X */
#define FLOPPY 0x00000001
#define CDROM  0x00000002
#define TAPE   0x00010000
X
#define MOUNTABLE(x) ((x) & 0x0000ffff)
X
static DEVTAB devtab[] = {
X    { "diskette", "/dev/fd0a", FLOPPY },
X    { "diskette0", "/dev/fd0a", FLOPPY },
X    { "diskette1", "/dev/fd1a", FLOPPY },
X    { "floppy", "/dev/fd0a", FLOPPY },
X    { "floppy0", "/dev/fd0a", FLOPPY },
X    { "floppy1", "/dev/fd1a", FLOPPY },
X    { "fd", "/dev/fd0a", FLOPPY },
X    { "fd0", "/dev/fd0a", FLOPPY },
X    { "fd1", "/dev/fd1a", FLOPPY },
X    { "cdrom", "/dev/cd0", CDROM },
X    { "cdrom0", "/dev/cd0", CDROM },
X    { "cdrom1", "/dev/cd1", CDROM },
X    { "cd", "/dev/cd0", CDROM },
X    { "cd0", "/dev/cd0", CDROM },
X    { "cd1", "/dev/cd1", CDROM },
X    { "mcd", "/dev/mcd0", CDROM },
X    { "mcd0", "/dev/mcd0", CDROM },
X    { "mcd1", "/dev/mcd1", CDROM },
X    { "tape", "/dev/rst0", TAPE },
X    { "tape0", "/dev/rst0", TAPE },
X    { "tape1", "/dev/rst1", TAPE },
X    { "st", "/dev/rst0", TAPE },
X    { "st0", "/dev/rst0", TAPE },
X    { "st1", "/dev/rst1", TAPE },
X    { "dat", "/dev/rst0", TAPE },
X    { "dat0", "/dev/rst0", TAPE },
X    { "dat1", "/dev/rst1", TAPE },
X    { "exabyte", "/dev/rst0", TAPE },
X    { "exabyte0", "/dev/rst0", TAPE },
X    { "exabyte1", "/dev/rst1", TAPE },
X    { NULL, NULL}
};
X  
/*
X * remind the syntax of the command to the user
X */  
static void 
usage()
{
X    errx(1, "usage: eject [-f][[-d] raw device | nickname ]");
X    /* NOTREACHED */
X 
} /* usage */
X
X
/*
X * given a device nick name, find its associated raw device and type
X */
static void
device_by_nickname(name, pdevice, pdevtype)
X    char *name;
X    char **pdevice;
X    int *pdevtype;
{
X    int i;
X
X    for (i = 0; devtab[i].name != NULL; i++) {
X	if (strcmp(name, devtab[i].name) == 0) {
X	    *pdevice = devtab[i].device;
X	    *pdevtype = devtab[i].type;
X	    return;
X	}
X    } /* for */
X    if (devtab[i].name == NULL) {
X	*pdevtype = -1;
X	*pdevice = NULL;
X    }
} /* device_by_nickname */
X
/* 
X * given a raw device name, find its type and remove the partition tag
X * from the name 
X */
static void
device_by_name(device, pdevtype)
X    char *device;
X    int *pdevtype;
{
X    int i;
X    
X    i = 0;
X    while (devtab[i].name != NULL) {
X	if (strncmp(devtab[i].device, device, strlen(devtab[i].device)) == 0) {
X	    *pdevtype = devtab[i].type;
X	    /* overwrite the device name - it's allways shorter */
X	    strcpy(device, devtab[i].device);
X	    return;
X	}
X	i++;
X    }
X    if (devtab[i].name == NULL) {
X	*pdevtype = -1;
X    }
} /* device_by_name */
X
/*
X * eject a floppy
X */
static void 
eject_floppy(device)
X    char *device;
{
#ifdef __i386
X    printf("You may now press the eject button on the floppy drive...\n");
#endif
#ifdef __sparc
X    int fd;
X
X    fd = open(device, O_RDONLY);
X    if (fd < 0) {
X	err(1, "open %s", device);
X    }
X    if (ioctl(fd, FDIOCEJECT, 0) < 0) {
X	err(1, device);
X    }
X    close(fd);
#endif
} /* eject_floppy */
X
/*
X * eject a cd 
X */
static void 
eject_cdrom(device)
X    char *device;
{
X    char full_name[MAXPATHLEN];
X    int fd;
X
X    sprintf(full_name, "%sd", device);
X    fd = open(full_name, O_RDONLY);
X    if (fd < 0) {
X	err(1, "open %s", full_name);
X    }
X    if (ioctl(fd, CDIOCALLOW) < 0) {
X	err(1, "%s: CDIOCALLOW", full_name);
X    }
X    if (ioctl(fd, CDIOCEJECT, 0) < 0) {
X	err(1, "%s: CDIOCEJECT", full_name);
X    }
X    close(fd);
} /* eject_cdrom */
X
/*
X * eject a tape
X */
static void
eject_tape(device)
X    char *device;
{
X    int fd;
X    struct mtop mt_com;
X    
X    fd = open(device, O_RDONLY);
X    if (fd < 0) {
X	err(1, "open %s", device);
X    }
X    mt_com.mt_op = MTOFFL;
X
X    if (ioctl(fd, MTIOCTOP, &mt_com) < 0) {
X	err(1, "%s:  MTOFFL", device);
X    }
X    close(fd);
} /* eject_tape */
X
/* 
X * test if  partitions of a device are mounted
X * and unmount them
X */
static void
umount_mounted(device)
X    char *device;
{
X    struct statfs *mntbuf;
X    int i, n, l;
X
X    n = getmntinfo(&mntbuf, MNT_NOWAIT);
X    if (n == 0) {
X	err(1, "getmntinfo");
X    }
X    l = strlen(device);
X    for (i = 0; i < n; i++) {
X	if (strncmp(device, mntbuf[i].f_mntfromname, l) == 0) {
X	    if (unmount(mntbuf[i].f_mntonname, 0) < 0) {
X		err(1, "umount %s from %s", mntbuf[i].f_mntfromname,
X		     mntbuf[i].f_mntonname);
X	    }
X	}
X    }  /* for */
X
} /* umount_mounted */
X    
/*
X * Eject - ejects various removable devices, including cdrom, tapes, 
X * diskettes 
X */
void
main(argc, argv)
X    int argc;
X    char *argv[];
{
X    extern char *optarg;
X    extern int optind;
X    char *device;
X    int umount_flag, devtype;
X    int ch;
X    
X    /* Default options values */
X    device = NULL;
X    devtype = -1;
X    umount_flag = 1;
X    
X    while ((ch = getopt(argc, argv, "d:f")) != EOF) {
X	switch (ch) {  
X	case 'd':      
X	    device = optarg;
X	    break;
X	case 'f':
X	    umount_flag = 0;
X	    break;
X	case '?':
X	default:
X	    usage();
X	    /* NOTREACHED */
X	} /* switch */
X    } /* while */
X
X    argc -= optind;
X    argv += optind;
X    
X    if (device != NULL) {
X	/* device specified with 'd' option */
X	device_by_name(device, &devtype);
X    } else {
X	if (argc > 0) {
X	    if (strncmp(argv[0], "/dev/", 5) == 0) {
X		/* if argument begins with /dev/, assume a device name */
X		device = argv[0];
X		device_by_name(argv[0], &devtype); 
X	    } else {
X		/* assume a nickname */
X		device_by_nickname(argv[0], &device, &devtype);
X	    }
X	} else {
X	    errx(1, "No device specified");
X	    /* NOTREACHED */
X	}
X    }
X    
X    if (devtype < 0) {
X	errx(1, "%s: unknown device", argv[0]);
X	/* NOTREACHED */
X    }
X    if (umount_flag && MOUNTABLE(devtype)) {
X	umount_mounted(device);
X    }     
X    switch (devtype) {
X    case FLOPPY:
X	eject_floppy(device);
X	break;
X    case CDROM:
X	eject_cdrom(device);
X	break;
X    case TAPE:
X	eject_tape(device);
X	break;
X    default:
X	errx(1, "impossible... devtype = %d", devtype);
X    } /* switch */
X    
X    exit (0);
} /* main */
X
X
X	
SHAR_EOF
  $shar_touch -am 0926185795 'eject.c' &&
  chmod 0644 'eject.c' ||
  echo 'restore of eject.c failed'
  shar_count="`wc -c < 'eject.c'`"
  test 8234 -eq "$shar_count" ||
    echo "eject.c: original size 8234, current size $shar_count"
fi
exit 0