com.zimbra.cs.localconfig.LocalConfigCLI.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.cs.localconfig.LocalConfigCLI.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc.
 *
 * This program 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,
 * version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program.
 * If not, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */

package com.zimbra.cs.localconfig;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.ConnectException;
import java.util.Collection;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.dom4j.DocumentException;

import com.zimbra.common.localconfig.ConfigException;
import com.zimbra.common.localconfig.ConfigWriter;
import com.zimbra.common.localconfig.KnownKey;
import com.zimbra.common.localconfig.LC;
import com.zimbra.common.localconfig.LC.Reloadable;
import com.zimbra.common.localconfig.LC.Supported;
import com.zimbra.common.localconfig.LocalConfig;
import com.zimbra.common.localconfig.Logging;
import com.zimbra.common.service.ServiceException;
import com.zimbra.common.soap.AdminConstants;
import com.zimbra.common.soap.SoapHttpTransport;
import com.zimbra.common.util.CliUtil;
import com.zimbra.common.util.RandomPassword;
import com.zimbra.common.util.ZimbraLog;
import com.zimbra.common.zclient.ZClientException;
import com.zimbra.cs.account.Provisioning;
import com.zimbra.cs.account.soap.SoapProvisioning;
import com.zimbra.soap.JaxbUtil;
import com.zimbra.soap.admin.message.ReloadLocalConfigRequest;

/**
 * zmlocalconfig CLI.
 */
public final class LocalConfigCLI {

    private final Options mOptions = new Options();

    private LocalConfigCLI() {
        mOptions.addOption("c", "config", true, "File in which configuration is stored.");
        mOptions.addOption("p", "path", false, "Show which configuration file will be used.");
        mOptions.addOption("e", "edit", false,
                "Edit configuration file, changing keys and values specified. [args] is in key=value form.");
        mOptions.addOption("r", "random", false,
                "Used with the edit option, sets specified key to random password string");
        mOptions.addOption("d", "default", false, "Show default values for keys listed in [args].");
        mOptions.addOption("n", "changed", false,
                "Show values for only those keys listed in [args] that have been changed from their defaults.");
        mOptions.addOption("i", "info", false, "Show documentation for keys listed in [args].");
        mOptions.addOption(null, "all", false, "Shows documentation for all keys, including unsupported ones");
        mOptions.addOption("x", "expand", false, "Expand values.");
        mOptions.addOption("s", "show", false, "Force display of password strings.");
        mOptions.addOption("f", "force", false,
                "Allow editing of keys whose change is known to be potentially dangerous.");
        mOptions.addOption("m", "format", true,
                "Show values in one of these formats: plain (default), xml, shell, export, nokey.");
        mOptions.addOption("q", "quiet", false, "Suppress logging.");
        mOptions.addOption("u", "unset", false,
                "Remove a configuration key.  If this is a key with compiled in defaults, set its value to the empty string.");
        mOptions.addOption("l", "reload", false, "Send a SOAP request to the server to reload its local config.");
        mOptions.addOption("h", "help", false, "Show this usage information.");

    }

    /**
     * Prints supported usage to stdout. Removes hidden options from the list to be printed
     *
     */
    private void usage() {
        Collection<Object> options = mOptions.getOptions();
        Options displayOptions = new Options();
        for (Object obj : options) {
            // copy over the supported options
            Option opt = (Option) obj; // should be a safe cast, this api REALLY needs generics
            if (!"all".equals(opt.getLongOpt())) {
                displayOptions.addOption(opt);
            }
        }
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("zmlocalconfig [options] [args]", "where [options] are:", displayOptions, "");
        System.exit(0);
    }

    private void checkNotRoot(String option) {
        String username = System.getProperty("user.name");
        if ("root".equalsIgnoreCase(username)) {
            error("cannot use " + option + " when running as root", null);
        }
    }

    private void error(String errmsg, Exception e) {
        Logging.error(errmsg, e);
        System.exit(1);
    }

    private void exec(String[] args) {
        CommandLine cl = null;
        CommandLineParser parser = new GnuParser();

        try {
            cl = parser.parse(mOptions, args);
        } catch (ParseException pe) {
            Logging.error("Failed to parse command line: " + pe);
            System.exit(1);
        }

        if (cl.hasOption("q")) {
            Logging.setQuietMode(true);
        }

        if (cl.hasOption("h")) {
            usage();
        }

        // Load known keys from BackupLC if available
        loadExtensionLC("com.zimbra.cs.backup.BackupLC");
        // Load known keys from ZimbraOpenOfficeExt if available
        loadExtensionLC("com.zimbra.openoffice.config.OpenOfficeLC");
        // Load known keys from ZimbraVoice if available
        loadExtensionLC("com.zimbra.cs.voice.VoiceLC");

        // info/docs for supported keys
        if (cl.hasOption("i")) {
            checkCompatibleOptions("i", "q", cl);
            LocalConfig.printDoc(System.out, cl.getArgs(), false);
            return;
        }
        // info/docs for all keys (hidden option)
        if (cl.hasOption("all")) {
            checkCompatibleOptions("all", "q", cl);
            LocalConfig.printDoc(System.out, cl.getArgs(), true);
            return;
        }

        LocalConfig lc = null;
        try {
            lc = new LocalConfig(cl.getOptionValue("c"));
        } catch (DocumentException de) {
            error("failed when reading config file", de);
        } catch (ConfigException ce) {
            error("failed with error in config file", ce);
        }

        // edit
        if (cl.hasOption("e")) {
            checkNotRoot("-e");
            checkCompatibleOptions("e", "qfrc", cl);
            String[] av = cl.getArgs();
            if (av == null || av.length == 0) {
                error("insufficient arguments", null);
            }
            for (int i = 0; i < av.length; i++) {
                String key = null;
                String value = null;
                if (cl.hasOption("r")) {
                    key = av[i];
                    value = RandomPassword.generate();
                } else {
                    int eqidx = av[i].indexOf("=");
                    if (eqidx <= 0) {
                        // <= 0 also catches first char being =, ie no key specified
                        error("argument '" + av[i] + "' not in key=value form", null);
                    }
                    key = av[i].substring(0, eqidx);
                    value = av[i].substring(eqidx + 1, av[i].length());
                }
                if (KnownKey.needForceToEdit(key) && !cl.hasOption("f")) {
                    error("can not edit key " + key, null);
                }
                lc.set(key, value);
            }
            try {
                lc.save();
            } catch (Exception e) {
                error("save to " + lc.getConfigFile() + " failed", e);
            }
            return;
        }

        // unset
        if (cl.hasOption("u")) {
            checkNotRoot("-u");
            checkCompatibleOptions("u", "qfc", cl);
            String[] av = cl.getArgs();
            if (av == null || av.length == 0) {
                error("insufficient arguments", null);
            }
            for (int i = 0; i < av.length; i++) {
                String key = av[i];
                if (!lc.isSet(key)) {
                    error("key " + key + " is not set", null);
                }
                lc.remove(key);
            }
            try {
                lc.save();
            } catch (Exception e) {
                error("save to " + lc.getConfigFile() + " failed", e);
            }
            return;
        }

        // show path
        if (cl.hasOption("p")) {
            checkCompatibleOptions("p", "qc", cl);
            System.out.println(lc.getConfigFile());
            return;
        }

        if (cl.hasOption("l")) {
            // reset logging and run native lib load
            CliUtil.toolSetup("WARN");
            try {
                reload();
            } catch (ServiceException e) {
                if (e.getCause() instanceof ConnectException) {
                    error("'" + Provisioning.SERVICE_MAILBOX + "' service is not running", null);
                } else {
                    error(e.getMessage(), e);
                }
            }
            return;
        }

        // print values
        String format = cl.getOptionValue("m");
        ConfigWriter cwriter = null;
        try {
            cwriter = ConfigWriter.getInstance(format, cl.hasOption("x"), !cl.hasOption("s"));
        } catch (ConfigException iae) {
            error("failed to create writer " + format, iae);
        }

        try {
            // changed
            if (cl.hasOption("n")) {
                checkCompatibleOptions("n", "qscmx", cl);
                lc.printChanged(System.out, cwriter, cl.getArgs());
                return;
            }

            // default
            if (cl.hasOption("d")) {
                checkCompatibleOptions("d", "qscmx", cl);
                lc.printDefaults(System.out, cwriter, cl.getArgs());
                return;
            }

            // current
            checkCompatibleOptions("", "qscmx", cl);
            lc.print(System.out, cwriter, cl.getArgs());
        } catch (Exception e) {
            error("exception occurred when printing", e);
        }
    }

    private void loadExtensionLC(String className) {
        try {
            Class<?> lcClass = Class.forName(className);
            Field[] fields = lcClass.getFields();
            for (Field field : fields) {
                try {
                    if (field.getType() == KnownKey.class) {
                        KnownKey key = (KnownKey) field.get(null);
                        // Automatically set the key name with the variable name.
                        key.setKey(field.getName());
                        // process annotations
                        if (field.isAnnotationPresent(Supported.class))
                            key.setSupported(true);
                        if (field.isAnnotationPresent(Reloadable.class))
                            key.setReloadable(true);
                    }
                } catch (Throwable never) {
                    // ignore
                }
            }
        } catch (ClassNotFoundException e) {
            // ignore
        }
    }

    private void checkCompatibleOptions(String mainOption, String compatibleOptions, CommandLine cl) {
        Option[] opts = cl.getOptions();
        for (int i = 0; i < opts.length; i++) {
            String clOption = opts[i].getOpt() == null ? opts[i].getLongOpt() : opts[i].getOpt();
            if (!mainOption.equals(clOption) && compatibleOptions.indexOf(clOption) == -1) {
                if (mainOption.equals("")) {
                    error("invalid option '" + clOption + "'", null);
                } else {
                    error("option '" + clOption + "' can not be used with option '" + mainOption + "'", null);
                }
            }
        }
    }

    private void reload() throws ServiceException {
        String host = LC.zimbra_zmprov_default_soap_server.value();
        int port = LC.zimbra_admin_service_port.intValue();
        SoapHttpTransport transport = new SoapHttpTransport(
                "https://" + host + ":" + port + AdminConstants.ADMIN_SERVICE_URI);

        SoapProvisioning prov = new SoapProvisioning();
        prov.soapSetURI(transport.getURI());
        prov.soapZimbraAdminAuthenticate();
        transport.setAuthToken(prov.getAuthToken());

        try {
            transport.invoke(JaxbUtil.jaxbToElement(new ReloadLocalConfigRequest()));
        } catch (IOException e) {
            throw ZClientException.IO_ERROR(e.getMessage(), e);
        }
    }

    public static void main(String[] args) {
        // Don't call CliUtil.toolSetup() until it's necessary as JNI libs
        // aren't in place in some cases during build.
        Logging.setUseZimbraLog(false);
        // Some of the things we call have ZimbraLog lines in them, setup a basic logger to stderr
        // to take care of those
        ZimbraLog.toolSetupLog4jConsole("WARN", true, false);
        new LocalConfigCLI().exec(args);
    }

}