package aQute.jpm.platform;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import aQute.jpm.lib.CommandData;
import aQute.jpm.lib.ServiceData;
import aQute.lib.getopt.Arguments;
import aQute.lib.getopt.Description;
import aQute.lib.getopt.Options;
import aQute.lib.io.IO;
import aQute.lib.strings.Strings;
import aQute.libg.command.Command;
import aQute.libg.glob.Glob;
public abstract class Unix extends Platform {
private final static Logger logger = LoggerFactory.getLogger(Unix.class);
public static String JPM_GLOBAL = "/var/jpm";
@Override
public File getGlobal() {
return new File("/var/jpm");
}
@Override
public File getGlobalBinDir() {
return new File("/usr/local/bin");
}
@Override
public File getLocal() {
File home = new File(System.getenv("HOME"));
return new File(home, "jpm");
}
@Override
public String createCommand(CommandData data, Map<String,String> map, boolean force, String... extra)
throws Exception {
data.bin = getExecutable(data);
File f = new File(data.bin);
if (f.isDirectory()) {
f = new File(data.bin, data.name);
data.bin = f.getAbsolutePath();
}
if (!force && f.exists())
return "Command already exists " + data.bin;
process("unix/command.sh", data, data.bin, map, extra);
return null;
}
@Override
public void deleteCommand(CommandData data) throws Exception {
File executable = new File(getExecutable(data));
IO.deleteWithException(executable);
}
@Override
public String createService(ServiceData data, Map<String,String> map, boolean force, String... extra)
throws Exception {
File initd = getInitd(data);
File launch = getLaunch(data);
if (!force) {
if (initd.exists())
return "Service already exists in " + initd + ", use --force to override";
if (launch.exists())
return "Service launch file already exists in " + launch + ", use --force to override";
}
process("unix/launch.sh", data, launch.getAbsolutePath(), map, add(extra, data.serviceLib));
process("unix/initd.sh", data, initd.getAbsolutePath(), map, add(extra, data.serviceLib));
return null;
}
public File getInitd(ServiceData data) {
return new File("/etc/init.d/" + data.name);
}
protected File getLaunch(ServiceData data) {
return new File(data.sdir, "launch.sh");
}
protected String getExecutable(CommandData data) {
return new File(jpm.getBinDir(), data.name).getAbsolutePath();
}
@Override
public String deleteService(ServiceData data) {
if (!getInitd(data).delete())
return "Cannot delete " + getInitd(data);
File f = new File(getExecutable(data));
if (!f.delete())
return "Cannot delete " + getExecutable(data);
System.out.println("Removed service data ");
return null;
}
@Override
public int launchService(ServiceData data) throws Exception {
File launch = getLaunch(data);
Process p = Runtime.getRuntime().exec(launch.getAbsolutePath(), null, new File(data.work));
return p.waitFor();
}
String DAEMON = "\n### JPM BEGIN ###\n" + "jpm daemon >" + JPM_GLOBAL + "/daemon.log 2>>"
+ JPM_GLOBAL + "/daemon.log &\n### JPM END ###\n";
static Pattern DAEMON_PATTERN = Pattern.compile("\n### JPM BEGIN ###\n.*\n### JPM END ###\n", Pattern.MULTILINE);
@Override
public void installDaemon(boolean user) throws Exception {
if (user)
throw new IllegalArgumentException("This Unix platform does not support user based agents");
File rclocal = new File("/etc/rc.d/rc.local");
if (!rclocal.isFile())
rclocal = new File("/etc/rc.local");
if (!rclocal.isFile())
throw new IllegalArgumentException("Cannot find rc.local in either /etc or /etc/rc.d. Unknown unix");
String s = IO.collect(rclocal);
if (s.contains(DAEMON))
return;
// TODO handle exit 0 at end of file
s += DAEMON;
IO.store(s, rclocal);
}
@Override
public void uninstallDaemon(boolean user) throws Exception {
if (user)
return;
File rclocal = new File("/etc/rc.d/rc.local");
if (!rclocal.isFile())
rclocal = new File("/etc/rc.local");
if (!rclocal.isFile())
return;
String s = IO.collect(rclocal);
Matcher m = DAEMON_PATTERN.matcher(s);
s = m.replaceAll("");
s += DAEMON;
IO.store(s, rclocal);
}
@Override
public void chown(String user, boolean recursive, File file) throws Exception {
String cmd = "chown " + (recursive ? " -R " : "") + user + " " + file.getAbsolutePath();
if ("root".equals(user))
return;
if ("0".equals(user))
return;
Command chown = new Command(cmd);
StringBuilder sb = new StringBuilder();
int n = chown.execute(sb, sb);
if (n != 0)
throw new IllegalArgumentException("Changing ownership for " + file + " fails: " + n + " : " + sb);
}
@Override
public String user() throws Exception {
ProcessBuilder pb = new ProcessBuilder();
Map<String,String> environment = pb.environment();
String user = environment.get("USER");
return user;
// Command id = new Command("id -nu");
// StringBuilder sb = new StringBuilder();
//
// int n = id.execute(sb,sb);
// if ( n != 0)
// throw new IllegalArgumentException("Getting user id fails: " + n +
// " : " + sb);
//
// return sb.toString().trim();
}
protected void process(String resource, CommandData data, String file, Map<String,String> map, String... extra)
throws Exception {
super.process(resource, data, file, map, extra);
run("chmod a+x " + file);
}
@Override
public void report(Formatter out) throws IOException, Exception {
out.format("Name \t%s\n", getName());
out.format("Global \t%s\n", getGlobal());
out.format("Local \t%s\n", getLocal());
}
/**
* Add the bindir to PATH env. variable in .profile
*/
@Arguments(arg = {})
@Description("Add the bin directory for this jpm to your PATH in the user's environment variables in the ~/.profile file.")
interface PathOptions extends Options {
@Description("Remove the bindir from the user's environment variables.")
boolean remove();
@Description("Delete a path from the PATH environment variable")
List<String> delete();
@Description("Add the current binary dir to the PATH environment variable")
boolean add();
@Description("Add additional paths to the PATH environment variable")
List<String> extra();
@Description("Override the default .profile and use this file. This file is relative to the home directory if not absolute.")
String profile();
@Description("If the profile file does not exist, create")
boolean force();
}
static Pattern PATH_P = Pattern.compile("(export|set)\\s+PATH\\s*=(.*):\\$PATH\\s*$");
@Description("Add the bin directory for this jpm to your PATH in the user's environment variables")
public void _path(PathOptions options) throws IOException {
File file = options.profile() == null ? IO.getFile("~/.profile") : IO.getFile(IO.home, options.profile());
if (!file.isFile()) {
if (!options.force()) {
reporter.error("No such file %s", file);
return;
}
IO.store("# created by jpm\n", file);
}
String parts[] = System.getenv("PATH").split(":");
List<String> paths = new ArrayList<String>(Arrays.asList(parts));
for (int i = 0; i < parts.length; i++) {
System.out.printf("%2d:%s %s %s%n", i, parts[i].toLowerCase().contains("jpm") ? "*" : " ",
new File(parts[i]).isDirectory() ? " " : "!", parts[i]);
}
String bd = jpm.getBinDir().getAbsolutePath();
String s = IO.collect(file);
//
// Remove the current binary path. If it is add, we add it later
//
if (options.remove() || options.add()) {
if (!bd.equals(getGlobalBinDir().getAbsolutePath())) {
s = s.replaceAll("(PATH\\s*=)" + bd + ":(.*\\$PATH)", "$1$2");
logger.debug("removed {}", bd);
}
}
if (options.delete() != null) {
for (String delete : options.delete()) {
s = s.replaceAll("(PATH\\s*=)" + Glob.toPattern(delete) + ":(.*\\$PATH)", "$1$2");
}
}
//
// Remove any orphans
//
s = s.replaceAll("\\s*(set|export)\\s+PATH\\s*=\\$PATH\\s*", "");
List<String> additions = new ArrayList<String>();
if (options.add()) {
if (!bd.equals(getGlobalBinDir().getAbsolutePath())) {
additions.add(bd);
}
}
if (options.extra() != null)
for (String add : options.extra()) {
File f = IO.getFile(add);
if (!f.isDirectory()) {
reporter.error("%s is not a directory, not added", f.getAbsolutePath());
} else {
if (!paths.contains(f.getAbsolutePath()))
additions.add(f.getAbsolutePath());
}
}
if (!additions.isEmpty()) {
s += "\nexport PATH=" + Strings.join(":", additions) + ":$PATH\n";
logger.debug("s {}", s);
}
IO.store(s, file);
}
}