483 lines
15 KiB
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);
|
|
}
|
|
}
|