/* * 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. * *

* This must be called before using any other functions in the library. It * may be called more than once with no effect. * *

* 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. * *

* This should be called before exiting your application. Make sure that all * drives you have grabbed are released before 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. * *

* Parameter value 1 enables a feature, 0 disables. Default is (1,0,0). * Have a good reason before you change it. * * @param exclusive *

* @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 * true to allow all implemented profiles, * false only tested media (default) */ public static void allowUntestedProfiles(boolean yes) { burn_allow_untested_profiles(yes); } /** * Scan for drives. * *

* No drives may be in use when this is called. All drive objects are * invalidated by using this function. Do NOT store drive * objects across calls to this function or death AND pain will ensue. * *

* 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(). * *

* 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. * *

* 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)} * *

* You are strongly urged to use this call whenever you know the * drive address in advance. * *

* If not, then you have to use directly above calls. In that case, you are * strongly urged 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()}. * *

* 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 * true 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. * *

* 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 * true on success, false 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. * *

* 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. * *

* 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 null 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()}. * *

* 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); * *

* 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 * null to not produce those messages. * @return * true if all went well, false 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()}. * *

* 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); * *

* Else you may eventually call {@link Drive#cancel()} on the active drive * and wait for it to assume state {@link DriveStatus#IDLE}. * *

* 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 * true if all went well, false 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. * *

* This forces the usage of the default signal handler, that will * eventually call {@link #abort(int, String) abort} and then perform * exit(1). * *

* 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 null 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); } }