eject.c revision ace1a5f11236a072fca1b5e0ea1416a083a9f2aa
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Program to eject one or more pieces of media.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
#include <libintl.h>
#include <unistd.h>
#include <pwd.h>
#include <volmgt.h>
#include <signal.h>
static int work(char *);
static void usage(void);
static char *getdefault(void);
static bool_t floppy_in_drive(char *, int);
static bool_t manually_ejectable(char *);
static char *eject_getfullblkname(char *, bool_t);
extern char *getfullrawname(char *);
/*
* ON-private libvolmgt routines
*/
extern int _dev_mounted(char *);
extern int _dev_unmount(char *);
extern char *_media_oldaliases(char *);
extern void _media_printaliases(void);
/*
* Hold over from old eject.
* returns exit codes: (KEEP THESE - especially important for query)
* 0 = -n, -d or eject operation was ok, -q = media in drive
* 1 = -q only = media not in drive
* 2 = various parameter errors, etc.
* 3 = eject ioctl failed
* New Value (2/94)
* 4 = eject partially succeeded, but now manually remove media
*/
#define EJECT_OK 0
#define EJECT_NO_MEDIA 1
#define EJECT_PARM_ERR 2
#define EJECT_IOCTL_ERR 3
#define EJECT_MAN_EJ 4
#define BIT_BUCKET "/dev/null"
#define EJECT_POPUP_PATH "/usr/dt/lib/eject_popup"
#define EJECT_POPUP "eject_popup"
#define OW_WINSYSCK_PATH "/usr/openwin/bin/winsysck"
#define OW_WINSYSCK "winsysck"
#define OW_WINSYSCK_PROTOCOL "x11"
#define AVAIL_MSG "%s is available\n"
#define NOT_AVAIL_MSG "%s is not available\n"
#define OK_TO_EJECT_MSG "%s can now be manually ejected\n"
#define FLOPPY_MEDIA_TYPE "floppy"
#define CDROM_MEDIA_TYPE "cdrom"
#define MEJECT_PROP "s-mejectable"
#define PROP_TRUE "true"
/*
* the call to winsysck() may hang if the xserver is running
* and there are no x-protocalls to be identified. This
* timeout is to break parent off of the waitpid() waiting on
* return of winsysck() which will never arrive
*/
#define TIMEOUT_ON_WAITPID 2
int
{
int c;
const char *opts = "dqfnp";
char *s;
int excode;
int res;
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
/* process arguments */
switch (c) {
case 'd':
do_pdefault = TRUE;
break;
case 'q':
break;
case 'n':
do_paliases = TRUE;
break;
case 'f':
force_eject = TRUE;
break;
case 'p':
break;
default:
usage();
}
}
if (do_pdefault) {
s = getdefault();
gettext("Default device is: %s\n"),
}
if (do_paliases) {
}
/* no argument -- use the default */
if ((s = getdefault()) == NULL) {
gettext("No default media available\n"));
}
/* (try to) eject default media */
} else {
/* multiple things to eject */
if (res == 4) {
}
}
if (err_seen) {
if (do_query)
else
} else if (man_eject_seen) {
} else {
}
}
return (excode);
}
/*
* eject's sig alarm
* This is to knock the parent process off of waitpid(), because
* if waitpid() fails to return within a couple seconds it never
* will.
* Therefor, we do nothing - just return...
*/
static void
ej_sig_alrm(int signo)
{
#ifdef lint
#endif
/* do nothing, return to interrupt the waitpid */
}
/*
* given a directory name find the first char- or block-spcl device under
* it
*
* return the supplied path if no spcl device found
*
* NOTE: return value points to a status area overwritten with
* each call
*/
static char *
getrawpart0(char *path)
{
int len;
int len_avail;
char *wptr;
/* set up the default and the work buffer */
return (NULL);
}
*wptr++ = '/';
/* scan directory */
continue;
}
/* XXX: just skip this entry?? */
continue;
}
/* success!! */
break;
}
}
}
}
return (res);
}
/*
* the the real work of ejecting (and notifying)
*/
static int
{
char *name;
char *name1;
/* keep track of if vold is or isn't running */
/*
* NOTE: media_findname (effectively) does a "volcheck all" if the
* name passed in isn't an absolute pathname, or a name under
* like "eject fd cd" on an intel, since the cd is "all but manually
* ejected" (as it should be), but then "cd" is not found, so
* a "volcheck all" is done, which remounts the floppy!
*
* So, we run media_olaliases() first, to try to find the name before
* running volcheck.
*
* on the other hand, if vold is *not* running, then we *just* do the
* old aliases
*/
/* check to see if name is an alias (e.g. "fd" or "flopy") */
}
if (volmgt_is_running) {
/*
* name is not an alias -- check for abs. path or
*/
/*
* name is not an alias, an absolute path, or a name
*/
} else {
/*
* we have to check for a directory name being
* returned from media_findname(), changing it
* to a devname if it is
*/
NULL) {
}
}
}
}
} else {
/* vold *not* running -- try to do what we can */
}
/*
* Since eject is a suid root program, we must make sure
* that the user running us is allowed to eject the media.
* All a user has to do to issue the eject ioctl is open
* the file for reading, so that's as restrictive as we'll be.
*/
/*
* Two cases for name. One is like "floppy", access() will fail
* for sure. If do_query==1, we should return EJECT_NO_MEDIA instead
* of EJECT_PARM_ERR. The other is like /vol/dev/rdiskette0 and
* access() will be ok since it is a directory
* in the schema of things for volmgmt, we should fail that the same
* way too.
*/
if (do_query) {
return (EJECT_NO_MEDIA);
} else {
return (EJECT_PARM_ERR);
}
}
if (do_query) {
/* don' t let directory name go through query() code */
return (EJECT_NO_MEDIA);
}
}
} else {
}
return (excode);
}
static void
usage(void)
{
gettext("usage: %s [-fndq] [name | nickname]\n"),
gettext("options:\t-f force eject\n"));
gettext("\t\t-n show nicknames\n"));
gettext("\t\t-d show default device\n"));
gettext("\t\t-q query for media present\n"));
gettext("\t\t-p do not call eject_popup\n"));
}
static int
{
int fd, r;
char path[MAXPATHLEN];
/*
* If volume management is either not running or not being managed by
* vold, and the device is mounted, we try to umount the device. If we
* fail, we give up, unless he used the -f flag.
*/
(void) seteuid(0);
r = _dev_unmount(name);
if (r == 0) {
if (!force_eject) {
gettext("WARNING: can not unmount %s, the file system is (probably) busy\n"),
name);
return (EJECT_PARM_ERR);
} else {
gettext("WARNING: %s has a mounted filesystem, ejecting anyway\n"),
name);
}
}
}
/*
* Require O_NDELAY for when floppy is not formatted
* will still id floppy in drive
*/
if (volume_is_not_managed) {
/*
* make sure we are dealing with a raw device
*
* XXX: NOTE: results from getfullrawname()
* really should be free()d when no longer
* in use
*/
}
gettext("%s is busy (try 'eject floppy' or 'eject cdrom'?)\n"),
name);
return (EJECT_PARM_ERR);
}
return (EJECT_PARM_ERR);
}
/* see if media is manually ejectable (i.e. we can't do it) */
if (volume_is_not_managed == FALSE) {
}
/* try to eject the volume */
/* check on why eject failed */
/* check for no floppy in manually ejectable drive */
/* use code below to handle "not present" */
}
/*
* Dump this message to stderr. This handles the
* case where the window system is not running
* and also works in case the user has run this
* via an rlogin to the remote volmgt console.
*/
}
(volume_is_not_managed || mejectable)) {
/*
* Make sure we know who *really* fired up this
* command. We'll need this information to connect
* to the user X display.
*/
#ifdef DEBUG
"DEBUG: ejectit: username = '%s'\n",
"DEBUG: ejectit: euid = %ld\n", geteuid());
"DEBUG: ejectit: egid = %ld\n", getegid());
} else {
"DEBUG: ejectit: getpwuid() failed\n");
}
#endif
/* if user doesn't want popup then stop here */
if (no_popup) {
}
/*
* If user is running some X windows system
* we'll display a popup to the console.
* If not, dump message to console.
*
* (To keep from having to actually check for X
* running, we'll just try to run the popup, assuming
* it will fail if windows are not running.)
*/
if (!do_manual_console_message) {
}
}
if (!do_manual_console_message) {
}
}
if (!do_manual_console_message) {
}
}
/*
* only output to console if requested and it's
* not the same as stderr
*/
if (do_manual_console_message) {
(void) seteuid(0);
if (console_fp != NULL) {
(void) fprintf(console_fp,
name);
(void) fclose(console_fp);
}
}
}
/*
* keep track of the fact that this is a manual
* ejection
*/
/*
* if our pathname is s slice (UFS is great) then
* check to see what really is busy
*/
}
gettext("%s not present in a drive\n"),
name);
} else {
}
}
return (result);
}
/*
* return TRUE if a floppy is in the drive, FALSE otherwise
*
* this routine assumes that the file descriptor passed in is for
* a floppy disk. this works because it's only called if the device
* is "manually ejectable", which only (currently) occurs for floppies.
*/
static bool_t
{
int ival = 0; /* ioctl return value */
if (!(ival & FDGC_CURRENT)) {
}
} else {
/* oh oh -- the ioctl failed -- it's not a floppy */
name);
}
return (rval);
}
/*
* display a "busy" message for the supplied pathname
*
* if the pathname is not a slice, then just display a busy message
* else if the pathname is some slice subdirectory then look for the
* *real* culprits
*
* if this is not done then the user can get a message like
* when they try to eject "cdrom0", but "s0" (e.g.) may be the only busy
* slice
*
* return TRUE iff we printed the appropriate error message, else
* return FALSE (and caller will print error message itself)
*/
static bool_t
{
char *blk; /* block name */
char *cp; /* for truncating path */
#ifdef DEBUG
#endif
/*
* get the block pathname.
* eject_getfullblkname returns NULL or pathname which
* has length < MAXPATHLEN.
*/
goto dun;
/* open mnttab for scanning */
/* can't open mnttab!? -- give up */
goto dun;
}
/* we found our entry -- we're done */
goto dun;
}
/* perhaps we have a sub-slice (which is what we exist to test for) */
/* create a base pathname */
/* no last slash in pathname!!?? -- give up */
goto dun;
}
*cp = '\0';
/* bblen = (uint)(cp - busy_base); */
/* scan for matches */
/*
* special device turns to NULL which isn't expected
*/
}
}
dun:
}
#ifdef DEBUG
#endif
errno = errno_save;
return (res);
}
/*
* In my experience with removable media drivers so far... the
* most reliable way to tell if a piece of media is in a drive
* is simply to open it. If the open works, there's something there,
* if it fails, there's not. We check for two errnos which we
* want to interpret for the user, ENOENT and EPERM. All other
* errors are considered to be "media isn't there".
*
* return TRUE if media found, else FALSE (XXX: was 0 and -1)
*/
static bool_t
{
int fd;
int rval; /* FDGETCHANGE return value */
enum dkio_state state;
if (doprint) {
}
} else {
if (doprint) {
name);
}
}
return (FALSE);
}
rval = 0;
/* hey, it worked, what a deal, it must be a floppy */
if (!(rval & FDGC_CURRENT)) {
if (doprint) {
name);
}
return (TRUE);
}
if (rval & FDGC_CURRENT) {
if (doprint) {
name);
}
return (FALSE);
}
}
/* great, the fancy ioctl is supported. */
if (state == DKIO_INSERTED) {
if (doprint) {
name);
}
return (TRUE);
}
if (state == DKIO_EJECTED) {
if (doprint) {
name);
}
return (FALSE);
}
/*
* Silly retry loop.
*/
(void) sleep(1);
goto again;
}
/*
* Ok, we've tried the non-blocking/ioctl route. The
* device doesn't support any of our nice ioctls, so
* we'll just say that if it opens it's there, if it
* doesn't, it's not.
*/
if (doprint) {
}
return (FALSE);
}
if (doprint) {
}
return (TRUE); /* success */
}
/*
* The assumption is that someone typed eject to eject some piece
* of media that's currently in a drive. So, what we do is
* check for floppy then cdrom. If there's nothing in either,
* we just return NULL.
*/
static char *
getdefault(void)
{
char *s;
/* if vold running then ask it about a floppy, then a cdrom */
if (volmgt_running()) {
return (s);
}
}
return (s);
}
}
}
/* no match yet -- try non-volmgt guesses */
return (s);
}
}
/* no default device */
return (NULL);
}
/*
* Check to see if the specified device is manually ejectable, using
* the media_getattr() call
*/
static bool_t
manually_ejectable(char *dev_path)
{
char *eprop;
/* equivalent to FALSE ? */
return (FALSE);
}
/* return result based on string returned */
}
/*
* Use a popup window to display the "manually ejectable"
* message for X86 machines.
*
* return flase if the popup fails, else return TRUE
*/
static bool_t
{
int exit_code = -1;
int fd;
char ld_lib_path[MAXPATHLEN];
char *home_dir;
/*
* fork a simple X Windows program to display gui for
* notifying the user that the specified media must be
* manually removed.
*/
gettext("error: can't fork a process (errno %d)\n"),
errno);
goto dun;
}
if (pid == 0) {
/*
* Error messages to console
*/
(void) seteuid(0);
if (fd >= 0) {
}
/*
* Set up the users environment.
*/
(void) putenv("DISPLAY=:0.0");
(void) putenv(ld_lib_path);
/*
* We need to set $HOME so the users .Xauthority file
* can be located. This is especially needed for a user
* user MIT Magic Cookie authentication security.
*/
perror("malloc");
exit(1);
}
/*
* We need the X application to be able to connect to
* the user's display so we better run as if we are
* the user (effectively).
* Don't want x program doing anything nasty.
*
* Note - have to set gid stuff first as effective uid
* must belong to root for this to work correctly.
*/
(void) seteuid(0);
#ifdef DEBUG
"DEBUG: \"%s\" being execl'ed with name = \"%s\"\n",
#endif
gettext("error: exec of \"%s\" failed (errno = %d)\n"),
exit(-1);
}
/* the parent -- wait for the child */
if (WEXITSTATUS(exit_code) == 0) {
}
}
}
dun:
/* all done */
#ifdef DEBUG
#endif
return (ret_val);
}
/*
* Use a popup window to display the "manually ejectable"
* message for X86 machines.
*
* return flase if the popup fails, else return TRUE
*/
static bool_t
{
int exit_code = -1;
int fd;
char *home_dir;
char ld_lib_path[MAXPATHLEN];
gettext("error: can't fork a process (errno %d)\n"),
errno);
goto dun;
}
if (pid == 0) {
/*
* error messages to console
*/
#ifndef DEBUG
}
#endif
/*
* set up the users environment
*/
(void) putenv("DISPLAY=:0.0");
(void) putenv(ld_lib_path);
/*
* we need to set $HOME so the users .Xauthority file
* can be located. This is especially needed for a user
* user MIT Magic Cookie authentication security
*/
perror("malloc");
exit(-1);
}
/*
* We need the X application to be able to connect to
* the user's display so we better run as if we are
* the user (effectively).
* Don't want x program doing anything nasty.
*
* Note - have to set gid stuff first as effective uid
* must belong to root for this to work correctly.
*/
(void) seteuid(0);
#ifdef DEBUG
"DEBUG: \"%s\" being execl'ed with protocol = \"%s\"\n",
#endif
gettext("error: exec of \"%s\" failed (errno = %d)\n"),
exit(-1);
}
/*
* parent:
* set up an alarm because the child may block forever
* when an x-protocol cannot be identified. This can
* happen on headless darwins (because they have a
* video board intergrated into its mother board).
*/
/* LINTED */
perror("signal(SIGALRM) error ");
return (ret_val);
}
/* the parent -- wait for the child or the alarm */
alarm(0);
if (WEXITSTATUS(exit_code) == 0) {
}
}
} else {
/* alarm went off, kill pid */
perror("kill(pid, SIGKILL) failed:\n");
}
}
dun:
/* all done */
#ifdef DEBUG
#endif
return (ret_val);
}
/*
* this routine will return the volmgt block name given the volmgt
* raw (char spcl) name
*
* if anything but a volmgt raw pathname is supplied that pathname will
* be returned
*
* NOTE: non-null return value will point to static data, overwritten with
* each call
*
* e.g. names starting with "/vol/r" will be changed to start with "/vol/",
*/
static char *
{
char raw_root[MAXPATHLEN];
const char *vm_root;
static char res_buf[MAXPATHLEN];
#ifdef DEBUG
#endif
/*
* try different strategies based on whether or not vold is running
*/
if (vm_running) {
/* vold IS running -- look in /vol (or its alternate) */
/* get vm root dir */
vm_root = volmgt_root();
/* get first volmgt root dev directory (and its length) */
/* see if we have a raw volmgt pathname (e.g. "/vol/r*") */
return (NULL);
}
goto dun; /* found match in /vol */
}
/* get second volmgt root dev directory (and its length) */
return (NULL);
}
}
} else {
/* vold is NOT running -- look in /dev */
return (NULL);
}
goto dun; /* found match in /dev */
}
}
/* no match -- return what we got */
dun:
#ifdef DEBUG
#endif
return (res_buf);
}