root/usr/src/cmd/cdrw/main.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <libintl.h>
#include <locale.h>
#include <dbus/dbus.h>
#include <hal/libhal.h>

#include "msgs.h"
#include "device.h"
#include "util.h"
#include "main.h"
#include "options.h"
#include "mmc.h"
#include "misc_scsi.h"

/*
 * global flags
 */
int     debug = 0;
int     keep_disc_open = 0;
int     requested_speed = 0;
int     simulation = 0;
int     verbose = 0;
char    *image_file = NULL;
char    *blanking_type = NULL;
int     audio_type = AUDIO_TYPE_NONE;
int     extract_track_no = 0;
char    *extract_file = NULL;
char    *alt_tmp_dir = NULL;
char    *copy_src = NULL;
int     vol_running = 0;
int     cflag = 0;
int     tflag = 0;
uid_t   ruid, cur_uid;

/*
 * global variables
 */
cd_device *target = NULL;               /* Default target device */
static char *tgtdev = NULL;
int device_type = CD_RW;                /* Default to CD/RW */
int write_mode = TAO_MODE;              /* Default to track at once */

static void
print_usage(void)
{
        err_msg(gettext("USAGE:\n"));
        err_msg(gettext("\tcdrw -i [ -vSCO ] [ -d device ] [ -p speed ]"));
        err_msg(gettext(" [ image-file ]\n"));
        err_msg(gettext("\tcdrw -a [ -vSCO ] [ -d device ] [ -p speed ]"));
        err_msg(gettext(" [ -T audio-type ] audio-file1 audio-file2 ...\n"));
        err_msg(gettext("\tcdrw -x [ -v ] [ -d device ] [ -T audio-type ]"));
        err_msg(gettext(" track-number audio-file\n"));
        err_msg(gettext("\tcdrw -c [ -SC ] [ -d device ] [ -p speed ]"));
        err_msg(gettext(" [ -m tmp-dir ] [ -s src-device ]\n"));
        err_msg(
            gettext("\tcdrw -b [ -v ] [ -d device ] all | session | fast\n"));
        err_msg(gettext("\tcdrw -M [ -v ] [ -d device ]\n"));
        err_msg(gettext("\tcdrw -L [ -v ] [ -d device ]\n"));
        err_msg(gettext("\tcdrw -l [ -v ]\n"));
        err_msg(gettext("\tcdrw -h\n"));

        exit(2);
}

static void
check_invalid_option(options *specified, char *opstr)
{
        options c_op;
        int ret;

        set_options_mask(&c_op, opstr);
        if ((ret = compare_options_mask(&c_op, specified)) != 0) {
                err_msg(
                    gettext("Option %c is not defined for this operation.\n"),
                    (char)ret);
                print_usage();
        }
}

LibHalContext *
attach_to_hald(void)
{
        LibHalContext *ctx = NULL;
        DBusConnection *con = NULL;
        DBusError error;
        hal_state_t state;

        /* Initialize the dbus error states */
        dbus_error_init(&error);

        if ((con = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == NULL) {
                return (NULL);
        }
        state = DBUS_CONNECTION;

        /* Allocate a new hal context to work with the dbus */
        if ((ctx = libhal_ctx_new()) == NULL)
                return (NULL);
        state = HAL_CONTEXT;

        /* Pair up the context with the connection */
        if (!libhal_ctx_set_dbus_connection(ctx, con))
                goto fail;
        state = HAL_PAIRED;

        /* If libhal_ctx_init fails hald is not present */
        if (!libhal_ctx_init(ctx, &error)) {
                goto fail;
        }
        state = HAL_INITIALIZED;

        return (ctx);
fail:
        if (dbus_error_is_set(&error))
                dbus_error_free(&error);
        detach_from_hald(ctx, state);
        return (NULL);

}

void
detach_from_hald(LibHalContext *ctx, hal_state_t state)
{
        DBusError error;
        DBusConnection *con = libhal_ctx_get_dbus_connection(ctx);

        dbus_error_init(&error);

        switch (state) {
        case HAL_INITIALIZED:
                if (libhal_ctx_shutdown(ctx, &error) == FALSE)
                        if (dbus_error_is_set(&error))
                                dbus_error_free(&error);
        /*FALLTHROUGH*/
        case HAL_PAIRED:
                (void) libhal_ctx_free(ctx);
                dbus_connection_unref(con);
                break;
        case HAL_CONTEXT:
                (void) libhal_ctx_free(ctx);
                break;
        case DBUS_CONNECTION:
        default:
                break;
        }
}

/*
 * This function returns one if hald is running and
 * zero if hald is not running
 */
int
hald_running(void)
{
        LibHalContext *ctx;

        if ((ctx = attach_to_hald()) == NULL)
                return (0);

        detach_from_hald(ctx, HAL_INITIALIZED);
        return (1);
}

int
setup_target(int flag)
{
        char *devpath;

        if (tgtdev != NULL) {
                devpath = (char *)my_zalloc(PATH_MAX);
                if (lookup_device(tgtdev, devpath)) {
                        target = get_device(tgtdev, devpath);
                }
                free(devpath);
                if (target == NULL) {
                        return (0);
                }
                return (1);
        }
        return (scan_for_cd_device(flag, &target));
}

int
main(int argc, char **argv)
{
        int             c;
        int             operations;
        options         specified_ops;
        int             aflag, iflag, Mflag, Lflag, lflag, bflag, xflag;
        int             ret;

        (void) setlocale(LC_ALL, "");

#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN     "SYS_TEST"
#endif


        (void) textdomain(TEXT_DOMAIN);

        ruid = getuid();
        cur_uid = geteuid();

        if (check_auth(ruid) != 1)  {
                err_msg(gettext(
                    "Authorization failed, Cannot access disks.\n"));
                exit(1);
        }

        if ((cur_uid == 0) && (ruid != 0)) {
                priv_change_needed = 1;
                lower_priv();
        }

        vol_running = hald_running();

        tgtdev = NULL;
        operations = 0;
        set_options_mask(&specified_ops, "");
        iflag = Mflag = Lflag = lflag = bflag = aflag = xflag = cflag = 0;

        while ((c = getopt(argc, argv, "abcCd:hiLlm:MOp:s:ST:vVx")) != EOF) {
                add_option(&specified_ops, c);
                switch (c) {
                case 'a':
                        aflag = 1;
                        operations++;
                        break;
                case 'b':
                        bflag = 1;
                        operations++;
                        break;
                case 'c':
                        cflag = 1;
                        operations++;
                        break;
                case 'C':
                        /*
                         * cdrw now attempts to use the stated medium capacity
                         * by default, so this option no longer has any effect.
                         * It remains in the interface for backwards
                         * compatibility only.
                         */
                        break;
                case 'd':
                        tgtdev = optarg;
                        break;
                case 'h':
                        print_usage(); /* will not return */
                        break;
                case 'i':
                        iflag = 1;
                        operations++;
                        break;
                case 'L':
                        Lflag = 1;
                        operations++;
                        break;
                case 'l':
                        lflag = 1;
                        operations++;
                        break;
                case 'm':
                        alt_tmp_dir = optarg;
                        break;
                case 'M':
                        Mflag = 1;
                        operations++;
                        break;
                case 'O':
                        keep_disc_open = 1;
                        break;
                case 'p':
                        requested_speed = atoi(optarg);
                        break;
                case 's':
                        copy_src = optarg;
                        break;
                case 'S':
                        simulation++;
                        break;
                case 'T':
                        audio_type = get_audio_type(optarg);
                        if (audio_type == -1) {
                                err_msg(gettext("Unknown audio type %s\n"),
                                    optarg);
                                exit(1);
                        }
                        break;
                case 'v':
                        verbose++;
                        break;
                case 'V':
                        /*
                         * more verbose. this will print out debug comments
                         */

                        debug++;
                        break;
                case 'x':
                        xflag++;
                        operations++;
                        break;
                default:
                        print_usage();
                }
        }
        if (operations == 0) {
                err_msg(gettext("No operation specified.\n"));
                exit(1);
        }
        if (operations != 1) {
                err_msg(gettext("More than one operation specified.\n"));
                exit(1);
        }

        if (lflag) {
                check_invalid_option(&specified_ops, "lhvV");
                list();
        }

        /*
         * we'll allow the user to specify the source device (-s) when
         *  extracting audio.
         */

        if (xflag && copy_src)
                tgtdev = copy_src;

        /*
         * This will scan for all CD devices when xflag or Mflag
         * (extract audio, list toc) commands are used, providing
         * no CD-RW devices are found. Since these commands can
         * be used without a CD writer.
         */

        if (xflag || Mflag) {
                ret = setup_target(SCAN_ALL_CDS);
        } else {
                ret = setup_target(SCAN_WRITERS);
        }

        if (ret == 0) {

                if (tgtdev != NULL) {
                        err_msg(gettext(
                            "Cannot find device %s.\n"), tgtdev);

                }

                if (vol_running) {
                        err_msg(gettext(
                            "No CD writers found or no media in the drive.\n"));
                } else {
                        if (cur_uid != 0) {
                                err_msg(gettext(
                                    "Volume manager is not running.\n"));
                                err_msg(gettext(
"Please start volume manager or run cdrw as root to access all devices.\n"));
                        } else {
                                err_msg(gettext(
                                    "No CD writers found.\n"));
                        }
                }
                exit(1);

        } else if (ret != 1) {
                err_msg(gettext("More than one CD device found.\n"));
                err_msg(gettext("Specify one using -d option.\n"));
                err_msg(gettext(
                    "Or use -l option to list all the CD devices found\n"));
                exit(1);
        }
        (void) check_device(target, CHECK_TYPE_NOT_CDROM|EXIT_IF_CHECK_FAILED);

        if (check_device(target, CHECK_NO_MEDIA) == 0) {
                int retry;
                for (retry = 0; retry < 5; retry++) {
                        if (check_device(target, CHECK_DEVICE_NOT_READY) == 0)
                                break;
                        (void) sleep(3);
                }
        }

        if (aflag) {
                check_invalid_option(&specified_ops, "ahvSCOdpTV");
                if (optind == argc) {
                        err_msg(gettext("No audio files specified.\n"));
                        exit(1);
                }
                write_audio(argv, optind, argc);
        }
        if (Mflag) {
                check_invalid_option(&specified_ops, "MhvdV");
                info();
        }
        if (iflag) {
                check_invalid_option(&specified_ops, "ihvSCOdpV");
                if (optind == (argc - 1)) {
                        image_file = argv[optind];
                        write_image();
                }
                if (optind == argc)
                        write_image();
                err_msg(gettext("Command line parsing error.\n"));
                err_msg(gettext("Only one image-file can be specified.\n"));
                exit(1);
        }
        if (bflag) {
                check_invalid_option(&specified_ops, "bhvdV");
                if (optind != (argc - 1)) {
                        err_msg(gettext("Command line parsing error.\n"));
                        print_usage();
                }
                blanking_type = argv[argc - 1];
                blank();
        }
        if (xflag) {
                check_invalid_option(&specified_ops, "xhpvdsTV");
                if (optind != (argc - 2)) {
                        err_msg(gettext("Command line parsing error.\n"));
                        print_usage();
                }
                extract_track_no = atoi(argv[argc - 2]);
                extract_file = argv[argc - 1];
                extract_audio();
        }
        if (cflag) {
                check_invalid_option(&specified_ops, "chvSCdpmsV");
                copy_cd();
        }

        /*
         * Open a closed disk, we do this by erasing the track tail
         * and then re-finalizing with an open leadout.
         */
        if (Lflag) {
                check_invalid_option(&specified_ops, "LvdV");
                (void) check_device(target, CHECK_NO_MEDIA |
                    CHECK_DEVICE_NOT_READY | EXIT_IF_CHECK_FAILED);

                /* no need to erase blank media */
                if (!check_device(target, CHECK_MEDIA_IS_NOT_BLANK))
                        exit(0);

                blanking_type = "leadout";
                blank();

                write_init(TRACK_MODE_DATA);
                (void) close_track(target->d_fd, 0, 1, 1);
                (void) finalize(target);
                (void) printf(gettext("done.\n"));
                exit(0);
        }
        return (0);
}