extras-legacy/java/trunk/src/java/org/pykix/libburnia/libburn/Burn.java

483 lines
15 KiB
Java

/*
* Burn.java
*
* Copyright (c) 2007 Vreixo Formoso
*
* This library 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.
* See COPYING file for details.
*/
package org.pykix.libburnia.libburn;
/**
* Library initialization/shutdown and miscellaneous functions.
*
*
* @author Vreixo Formoso
* @since 0.1
*/
public class Burn {
static final String JNI_LIBRARY_NAME = "java-libburn-0.1";
/** Maximum number of particularly permissible drive addresses. */
public static final int WHITELIST_LEN = 255;
/**
* Initialize the library.
*
* <p>
* This must be called before using any other functions in the library. It
* may be called more than once with no effect.
*
* <p>
* It is possible to 'restart' the library by shutting it down and
* re-initializing it. This is necessary if you follow the older and
* more general way of accessing a drive via burn_drive_scan() and
* burn_drive_grab(). See burn_drive_scan_and_grab() with its strong
* urges and its explanations. TODO fix this comment
*
* @throws BurnException
* If library initialization fails.
*/
public static void initialize() throws BurnException {
if ( burn_initialize() == 0 ) {
throw new BurnException("Can't initialize libburnia library");
}
}
/**
* Shutdown the library.
*
* <p>
* This should be called before exiting your application. Make sure that all
* drives you have grabbed are released <i>before</i> calling this.
*/
public static void finish() {
burn_finish();
}
/**
* Set parameters for behavior on opening device files. To be called early
* after {@link #initialize()} and before any bus scan. But not mandatory at
* all.
*
* <p>
* Parameter value 1 enables a feature, 0 disables. Default is (1,0,0).
* Have a good reason before you change it.
*
* @param exclusive
* <ul>
* <li>1 = Try to open only devices which are not marked as busy
* and try to mark them busy if opened sucessfully. (O_EXCL)
* There are kernels which simply don't care about O_EXCL.
* Some have it off, some have it on, some are switchable.
* <li>2 = in case of a SCSI device, also try to open exclusively
* the matching /dev/sr, /dev/scd and /dev/st .
* <li>0 = no attempt to make drive access exclusive.
* </ul>
* @param blocking
* Try to wait for drives which do not open immediately but
* also do not return an error as well. (O_NONBLOCK)
* This might stall indefinitely with /dev/hdX hard disks.
* @param abortOnBusy
* Unconditionally abort process when a non blocking
* exclusive opening attempt indicates a busy drive.
* Use this only after thorough tests with your app.
*/
public static void presetDeviceOpen(int exclusive, int blocking,
int abortOnBusy) {
burn_preset_device_open(exclusive, blocking, abortOnBusy);
}
/**
* Allows the use of media types which are implemented in libburn but not
* yet tested. The list of those untested profiles is subject to change.
* Currently it contains: 0x15 "DVD-R/DL Sequential".
* If you really test such media, then please report the outcome on
* libburn-hackers@pykix.org
* If ever then this call should be done soon after {@link #initialize()}
* before any drive scanning.
*
* @param yes
* <code>true</code> to allow all implemented profiles,
* <code>false</code> only tested media (default)
*/
public static void allowUntestedProfiles(boolean yes) {
burn_allow_untested_profiles(yes);
}
/**
* Scan for drives.
*
* <p>
* No drives may be in use when this is called. All drive objects are
* invalidated by using this function. Do <b>NOT</b> store drive
* objects across calls to this function or death AND pain will ensue.
*
* <p>
* After this call all drives depicted by the returned array are subject
* to eventual (O_EXCL) locking. See burn_preset_device_open(). This state
* ends either with burn_drive_info_forget() or with burn_drive_release().
*
* <p>
* It is unfriendly to other processes on the system to hold drives locked
* which one does not definitely plan to use soon.
*
* @return
* an array of drive info items (cdroms/burners).
* The returned array must be freed by burn_drive_info_free()
* before burn_finish(), and also before calling this function
* burn_drive_scan() again.
* @throws BurnException
* If scan fails
*
* TODO correct above comment
*/
public static DriveInfo[] scan() throws BurnException {
DriveInfo[] infos = burn_drive_scan();
if ( infos == null ) {
throw new BurnException("No drives found");
}
return infos;
}
/**
* Aquire a drive with known persistent address.
*
* <p>
* This is the sysadmin friendly way to open one drive and to leave all
* others untouched. It bundles the following API calls to form a
* non-obtrusive way to use libburn:
* burn_drive_add_whitelist() , {@link #scan()}, {@link Drive#grab(boolean)}
*
* <p>
* You are <b>strongly urged</b> to use this call whenever you know the
* drive address in advance.
*
* <p>
* If not, then you have to use directly above calls. In that case, you are
* <b>strongly urged</b> to drop any unintended drive which will be
* exclusively occupied and not closed by {@link #scan()}.
* This can be done by shutting down the library including a call to
* {@link #finish()}. You may later start a new libburn session and should
* then use the function described here with an address obtained after
* {@link #scan()} via {@link DriveInfo#getAdr()}.
*
* <p>
* Another way is to drop the unwanted drives by {@link DriveInfo#forget(int)}.
*
* @param adr
* The persistent address of the desired drive. Either obtained
* by {@link DriveInfo#getAdr()} or guessed skillfully by application
* resp. its user.
* @param load
* <code>true</code> to make the drive attempt to load a disc (close
* its tray door, etc).
* @return
* A DriveInfo object with the requested cdrom/burner.
* @throws BurnException
* If drive not found.
*/
public static DriveInfo scanAndGrab(String adr, boolean load)
throws BurnException {
DriveInfo info = burn_drive_scan_and_grab(adr, load);
if ( info == null ) {
throw new BurnException("Drive not found");
}
return info;
}
/**
* Add a device to the list of permissible drives. As soon as some entry
* is in the whitelist all non-listed drives are banned from scanning.
*
* <p>
* The number of devices that can be added to the whitelist is limited
* to {@link #WHITELIST_LEN}.
*
* @param adr
* The persistent address of the drive.
* @return
* <code>true</code> on success, <code>false</code> otherwise.
*/
public static boolean addWhiteList(String adr) {
return (burn_drive_add_whitelist(adr) == 1);
}
/**
* Remove all drives from whitelist. This enables all possible drives.
*/
public static void clearWhiteList() {
burn_drive_clear_whitelist();
}
/**
* Set the verbosity level of the library.
*
* <p>
* The default value is 0, which means that nothing is output on stderr.
* The more you increase this, the more debug output should be displayed
* on stderr for you.
*
* <p>
* This is for development only. Not suitable for applications.
*
* @param level
* The verbosity level desired. 0 for nothing, higher positive
* values for more information output.
*/
public static void setVerbosity(int level) {
burn_set_verbosity(level);
}
/**
* Evaluate wether the given address would be a possible persistent
* drive address of libburn.
*/
public static boolean isEnumerableAdr(String adr) {
return burn_drive_is_enumerable_adr(adr);
}
/**
* Try to convert a given existing filesystem address into a persistent
* drive address. This succeeds with symbolic links or if a hint about
* the drive's system address can be read from the filesystem object and
* a matching drive is found.
*
* @param path
* The address of an existing file system object
* @return
* The persistent address or <code>null</code> on failure
*/
public static String convertFsAdr(String path) {
return burn_drive_convert_fs_adr(path);
}
/**
*
*/
public interface AbortHandler {
/**
* Function suitable to produce appeasing messages while
* burn is been canceled.
*
* @param patience
* Maximum number of seconds to wait.
* @param elapsed
* Elapsed number of seconds.
* @return
*/
public int abortPacifier(int patience, int elapsed);
}
/**
* Abort any running drive operation and finally call {@link #finish()}.
*
* <p>
* You MUST calm down the busy drive if an aborting event occurs during a
* burn run. For that you may call this function either from your own signal
* handling code or indirectly by activating the builtin signal handling:
* burn_set_signal_handling("my_app_name : ", NULL, 0);
*
* <p>
* Else you may eventually call {@link Drive#cancel()} on the active drive
* and wait for it to assume state {@link DriveStatus#IDLE}.
*
* @param patience
* Maximum number of seconds to wait for drives to finish
* @param pacifier
* A function to produce appeasing messages. You can pass
* <code>null</code> to not produce those messages.
* @return
* <code>true</code> if all went well, <code>false</code> if had
* to leave a drive in unclean state
* @throws BurnException
* On severe error, do no use libburn again
* @see #abort(int, String)
*/
public static boolean abort(int patience, AbortHandler pacifier)
throws BurnException {
return burn_abort_java(patience, pacifier);
}
/**
* Abort any running drive operation and finally call {@link #finish()}.
*
* <p>
* You MUST calm down the busy drive if an aborting event occurs during a
* burn run. For that you may call this function either from your own signal
* handling code or indirectly by activating the builtin signal handling:
* burn_set_signal_handling("my_app_name : ", NULL, 0);
*
* <p>
* Else you may eventually call {@link Drive#cancel()} on the active drive
* and wait for it to assume state {@link DriveStatus#IDLE}.
*
* <p>
* This version of abort uses the default pacifier function that print
* messages to stdout.
*
* @param patience
* Maximum number of seconds to wait for drives to finish
* @param msg
* The message to be printed.
* @return
* <code>true</code> if all went well, <code>false</code> if had
* to leave a drive in unclean state
* @throws BurnException
* On severe error, do no use libburn again
* @see #abort(int, org.pykix.libburnia.libburn.Burn.AbortHandler)
*/
public static boolean abort(int patience, String msg) throws BurnException {
return burn_abort_def(patience, msg);
}
/**
* Interface suitable for {@link Burn#setSignalHandler(org.pykix.libburnia.libburn.Burn.SignalHandler, int)}
*/
public interface SignalHandler {
/**
* Function to be called when a signal is received.
*
* @param signum
* @param flag
* @return
* has to return -2 if it does not want the process to
* exit with value 1.
*/
public int handleSignal(int signum, int flag);
}
/**
* Control builtin signal handling. See also
* {@link #abort(int, org.pykix.libburnia.libburn.Burn.AbortHandler)}.
*
* @param handler
* A handler to be called on signals. It's
* {@link SignalHandler#handleSignal(int, int) handleSignal} method
* will be called when a signal is received. It should finally call
* {@link #abort(int, org.pykix.libburnia.libburn.Burn.AbortHandler)}.
* @param mode
* 0 call handler(signum, 0) on nearly all signals
* 1 enable system default reaction on all signals
* 2 try to ignore nearly all signals
* 10 like mode 2 but handle SIGABRT like with mode 0
*/
public static void setSignalHandler(SignalHandler handler, int mode) {
if ( handler == null ) {
throw new NullPointerException("Handler cannot be null");
}
burn_set_signal_handling(handler, mode);
}
/**
* Control builtin signal handling.
*
* <p>
* This forces the usage of the default signal handler, that will
* eventually call {@link #abort(int, String) abort} and then perform
* exit(1).
*
* <p>
* If you want to provide a custom signal handler you can use
* {@link #setSignalHandler(org.pykix.libburnia.libburn.Burn.SignalHandler, int)}
*
* @param msg
* If not <code>null</code> then it is used as prefix for pacifier
* messages.
* @param mode
* 0 call handler(signum, 0) on nearly all signals
* 1 enable system default reaction on all signals
* 2 try to ignore nearly all signals
* 10 like mode 2 but handle SIGABRT like with mode 0
*/
public static void setDefaultSignalHandler(String msg, int mode) {
burn_set_default_signal_handling(msg, mode);
}
public static class Version {
public int major;
public int minor;
public int micro;
public Version(int major, int minor, int micro) {
super();
this.major = major;
this.minor = minor;
this.micro = micro;
}
@Override
public String toString() {
return major + "." + minor + "." + micro;
}
}
/**
* Returns the library's version.
*/
public static Version getVersion() {
return burn_version();
}
private static native int burn_initialize();
private static native void burn_finish();
private static native void burn_preset_device_open(int exclusive,
int blocking, int abort_on_busy);
private static native void burn_allow_untested_profiles(boolean yes);
private static native DriveInfo[] burn_drive_scan();
private static native DriveInfo burn_drive_scan_and_grab(String adr,
boolean load);
private static native int burn_drive_add_whitelist(String adr);
private static native void burn_drive_clear_whitelist();
private static native void burn_set_verbosity(int level);
private static native boolean burn_drive_is_enumerable_adr(String adr);
private static native String burn_drive_convert_fs_adr(String path);
private static native boolean burn_abort_def(int patience, String msg)
throws BurnException;
//TODO not tested at all!!
private static native boolean burn_abort_java(int patience, AbortHandler h)
throws BurnException;
private static native void burn_set_signal_handling(SignalHandler handler,
int mode);
private static native void burn_set_default_signal_handling(String msg,
int mode);
private static native Version burn_version();
static {
System.loadLibrary(JNI_LIBRARY_NAME);
}
}