pax_global_header 0000666 0000000 0000000 00000000064 12077566702 0014526 g ustar 00root root 0000000 0000000 52 comment=8beda2b1791417e7a3e4fb69369ce45722ac8bd8
akuma-akuma-1.9/ 0000775 0000000 0000000 00000000000 12077566702 0013571 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/.gitignore 0000664 0000000 0000000 00000000010 12077566702 0015550 0 ustar 00root root 0000000 0000000 /target/ akuma-akuma-1.9/pom.xml 0000664 0000000 0000000 00000004743 12077566702 0015116 0 ustar 00root root 0000000 0000000
4.0.0
org.kohsuke
pom
2
akuma
1.9
Embeddable daemonization library
scm:git:git@github.com/kohsuke/${project.artifactId}.git
scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git
http://${project.artifactId}.kohsuke.org/
github-pages
gitsite:git@github.com/kohsuke/${project.artifactId}.git
maven-assembly-plugin
attached
package
jar-with-dependencies
com.sun.akuma.EchoServer
net.java.dev.jna
jna
3.4.0
maven-jxr-plugin
m.g.o-public
http://maven.glassfish.org/content/groups/public/
m.g.o-public
http://maven.glassfish.org/content/groups/public/
MIT license
http://www.opensource.org/licenses/mit-license.php
repo
akuma-akuma-1.9/src/ 0000775 0000000 0000000 00000000000 12077566702 0014360 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/src/main/ 0000775 0000000 0000000 00000000000 12077566702 0015304 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/src/main/java/ 0000775 0000000 0000000 00000000000 12077566702 0016225 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/src/main/java/com/ 0000775 0000000 0000000 00000000000 12077566702 0017003 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/src/main/java/com/sun/ 0000775 0000000 0000000 00000000000 12077566702 0017610 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/src/main/java/com/sun/akuma/ 0000775 0000000 0000000 00000000000 12077566702 0020706 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/src/main/java/com/sun/akuma/CLibrary.java 0000664 0000000 0000000 00000006515 12077566702 0023267 0 ustar 00root root 0000000 0000000 /*
* The MIT License
*
* Copyright (c) 2009-, Sun Microsystems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sun.akuma;
import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.NativeLong;
import com.sun.jna.StringArray;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.PointerType;
import com.sun.jna.ptr.IntByReference;
/**
* GNU C library.
*/
public interface CLibrary extends Library {
int fork();
int kill(int pid, int signum);
int setsid();
int setuid(short newuid);
int setgid(short newgid);
int umask(int mask);
int getpid();
int getppid();
int chdir(String dir);
int execv(String file, StringArray args);
int setenv(String name, String value);
int unsetenv(String name);
void perror(String msg);
String strerror(int errno);
// this is listed in http://developer.apple.com/DOCUMENTATION/Darwin/Reference/ManPages/man3/sysctlbyname.3.html
// but not in http://www.gnu.org/software/libc/manual/html_node/System-Parameters.html#index-sysctl-3493
// perhaps it is only supported on BSD?
int sysctlbyname(String name, Pointer oldp, IntByReference oldlenp, Pointer newp, IntByReference newlen);
int sysctl(int[] mib, int nameLen, Pointer oldp, IntByReference oldlenp, Pointer newp, IntByReference newlen);
int sysctlnametomib(String name, Pointer mibp, IntByReference size);
public class FILE extends PointerType {
public FILE() {
}
public FILE(Pointer pointer) {
super(pointer);
}
}
// Additional C functions we need on Solaris 64bit to seek to a place above Long.MAX_VALUE
FILE fopen(String fileName, String mode);
int fseek(FILE file, long offset, int whence);
long ftell(FILE file);
int fread(Pointer buf, int size, int count, FILE file);
int fclose(FILE file);
/**
* Read a symlink. The name will be copied into the specified memory, and returns the number of
* bytes copied. The string is not null-terminated.
*
* @return
* if the return value equals size, the caller needs to retry with a bigger buffer.
* If -1, error.
*/
int readlink(String filename, Memory buffer, NativeLong size);
public static final CLibrary LIBC = (CLibrary) Native.loadLibrary("c",CLibrary.class);
}
akuma-akuma-1.9/src/main/java/com/sun/akuma/Daemon.java 0000664 0000000 0000000 00000022227 12077566702 0022761 0 ustar 00root root 0000000 0000000 /*
* The MIT License
*
* Copyright (c) 2009-, Sun Microsystems, Inc., CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sun.akuma;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.StringArray;
import static com.sun.akuma.CLibrary.LIBC;
import java.io.FileWriter;
import java.io.IOException;
import java.io.File;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Forks a copy of the current process into the background.
*
*
* Because of the fork/exec involved in doing this, your code has to call Daemonizer in a certain sequence.
* Specifically, from your main method:
*
* public static void main(String[] args) {
* Daemon d = new Daemon();
* if(d.isDaemonized()) {
* // perform initialization as a daemon
* // this involves in closing file descriptors, recording PIDs, etc.
* d.{@linkplain #init() init}();
* } else {
* // if you are already daemonized, no point in daemonizing yourself again,
* // so do this only when you aren't daemonizing.
* if(you decide to launch a copy into background) {
* d.daemonize(...);
* System.exit(0);
* }
* }
*
* // your normal main code follows
* // this part can be executed in two ways
* // 1) the user runs your process in the foreground
* // 2) you decided to daemonize yourself, in which case the newly forked daemon will execute this code,
* // while the originally executed foreground Java process exits before it gets here.
* ...
* }
*
*
*
* Alternatively, your main class can extend from Daemon, so that you can customize some of the behaviors.
*
* @author Kohsuke Kawaguchi
*/
public class Daemon {
/**
* Do all the necessary steps in one go.
*
* @param daemonize
* Parse the command line arguments and if the application should be
* daemonized, pass in true.
*/
public void all(boolean daemonize) throws Exception {
if(isDaemonized())
init();
else {
if(daemonize) {
daemonize();
System.exit(0);
}
}
}
/**
* Returns true if the current process is already launched as a daemon
* via {@link #daemonize()}.
*/
public boolean isDaemonized() {
return System.getProperty(Daemon.class.getName())!=null;
}
/**
* Relaunches the JVM with the exact same arguments into the daemon.
*/
public void daemonize() throws IOException {
daemonize(JavaVMArguments.current());
}
/**
* Relaunches the JVM with the given arguments into the daemon.
*/
public void daemonize(JavaVMArguments args) {
if(isDaemonized())
throw new IllegalStateException("Already running as a daemon");
if (System.getProperty("com.sun.management.jmxremote.port") != null) {
try {
Method m = Class.forName("sun.management.Agent").getDeclaredMethod("stopRemoteManagementAgent");
m.setAccessible(true);
m.invoke(null);
} catch (Exception x) {
LOGGER.log(Level.SEVERE, "could not simulate jcmd $$ ManagementAgent.stop (JENKINS-14529)", x);
}
}
// let the child process now that it's a daemon
args.setSystemProperty(Daemon.class.getName(),"daemonized");
// prepare for a fork
String exe = getCurrentExecutable();
StringArray sa = args.toStringArray();
int i = LIBC.fork();
if(i<0) {
LIBC.perror("initial fork failed");
System.exit(-1);
}
if(i==0) {
// with fork, we lose all the other critical threads, to exec to Java again
LIBC.execv(exe,sa);
System.err.println("exec failed");
LIBC.perror("initial exec failed");
System.exit(-1);
}
// parent exits
}
/**
* Overwrites the current process with a new Java VM with the given JVM arguments.
*/
public static void selfExec(JavaVMArguments args) {
LIBC.execv(getCurrentExecutable(), args.toStringArray());
}
/**
* Prepares the current process to act as a daemon.
* The daemon's PID is written to the file /var/run/daemon.pid
.
*/
public void init() throws Exception {
init("/var/run/daemon.pid");
}
/**
* Prepares the current process to act as a daemon.
* @param pidFile the filename to which the daemon's PID is written;
* or, null
to skip writing a PID file.
*/
@SuppressWarnings({"OctalInteger"})
public void init(String pidFile) throws Exception {
// start a new process session
LIBC.setsid();
closeDescriptors();
chdirToRoot();
if (pidFile != null) writePidFile(pidFile);
}
/**
* Closes inherited file descriptors.
*
*
* This method can be overridden to no-op in a subtype. Useful for debugging daemon processes
* when they don't work correctly.
*/
protected void closeDescriptors() throws IOException {
if(!Boolean.getBoolean(Daemon.class.getName()+".keepDescriptors")) {
System.out.close();
System.err.close();
System.in.close();
}
// ideally we'd like to close all other descriptors, but that would close
// jar files used as classpath, and break JVM.
}
/**
* change directory to '/' to avoid locking directories.
*/
protected void chdirToRoot() {
LIBC.chdir("/");
System.setProperty("user.dir","/");
}
/**
* Writes out the PID of the current process to the specified file.
* @param pidFile the filename to write the PID to.
*/
protected void writePidFile(String pidFile) throws IOException {
try {
FileWriter fw = new FileWriter(pidFile);
fw.write(String.valueOf(LIBC.getpid()));
fw.close();
} catch (IOException e) {
// if failed to write, keep going because maybe we are run from non-root
}
}
/**
* Gets the current executable name.
*/
public static String getCurrentExecutable() {
int pid = LIBC.getpid();
String name = "/proc/" + pid + "/exe";
File exe = new File(name);
if(exe.exists()) {
try {
String path = resolveSymlink(exe);
if (path!=null) return path;
} catch (IOException e) {
LOGGER.log(Level.FINE,"Failed to resolve symlink "+exe,e);
}
return name;
}
// cross-platform fallback
return System.getProperty("java.home")+"/bin/java";
}
private static String resolveSymlink(File link) throws IOException {
String filename = link.getAbsolutePath();
for (int sz=512; sz < 65536; sz*=2) {
Memory m = new Memory(sz);
int r = LIBC.readlink(filename,m,new NativeLong(sz));
if (r<0) {
int err = Native.getLastError();
if (err==22/*EINVAL --- but is this really portable?*/)
return null; // this means it's not a symlink
throw new IOException("Failed to readlink "+link+" error="+ err+" "+ LIBC.strerror(err));
}
if (r==sz)
continue; // buffer too small
byte[] buf = new byte[r];
m.read(0,buf,0,r);
return new String(buf);
}
throw new IOException("Failed to readlink "+link);
}
/**
* Flavor of {@link Daemon} that doesn't change the current directory.
*
*
* This turns out to be often more useful as JavaVM can take lot of arguments and system properties
* that use paths, and when we CD they won't work.
*/
public static class WithoutChdir extends Daemon {
@Override
protected void chdirToRoot() {
// noop
}
}
private static final Logger LOGGER = Logger.getLogger(Daemon.class.getName());
}
akuma-akuma-1.9/src/main/java/com/sun/akuma/EchoServer.java 0000664 0000000 0000000 00000005671 12077566702 0023627 0 ustar 00root root 0000000 0000000 /*
* The MIT License
*
* Copyright (c) 2009-, Sun Microsystems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sun.akuma;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.OutputStream;
import static com.sun.akuma.CLibrary.LIBC;
/**
* Sample echo server.
*
* @author Kohsuke Kawaguchi
*/
public class EchoServer extends NetworkServer {
public static void main(String[] args) throws Exception {
new EchoServer(args).run();
}
public EchoServer(String[] args) {
super(args);
}
/**
* Daemonize if something is given as arguments.
*/
@Override
protected boolean shouldBeDaemonized() {
return !arguments.isEmpty();
}
@Override
protected void frontend() throws Exception {
System.out.println("This is a simple echo server. Run with some argument to fork into a daemon, then try 'nc localhost 12345' from several terminals.");
super.frontend();
}
@Override
protected void forkWorkers(JavaVMArguments args) throws Exception {
// TODO: parse arguments and decide how many to fork
forkWorkerThreads(args, 2);
}
@Override
protected ServerSocket createServerSocket() throws Exception {
System.out.println("Listening on port 12345");
// TODO: parse arguments and decide port
return new ServerSocket(12345);
}
@Override
protected void worker(ServerSocket ss) throws Exception {
byte[] buf = new byte[1024];
// run a simple echo server
while(true) {
Socket s = ss.accept();
System.out.println("PID:"+ LIBC.getpid()+" accepted a new connection");
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
int len;
while((len=in.read(buf))>=0)
out.write(buf,0,len);
s.close();
}
}
}
akuma-akuma-1.9/src/main/java/com/sun/akuma/JavaVMArguments.java 0000664 0000000 0000000 00000044346 12077566702 0024576 0 ustar 00root root 0000000 0000000 /*
* The MIT License
*
* Copyright (c) 2009-, Sun Microsystems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sun.akuma;
import com.sun.jna.StringArray;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import static com.sun.jna.Pointer.NULL;
import com.sun.jna.ptr.IntByReference;
import java.util.*;
import static java.util.logging.Level.FINEST;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.logging.ConsoleHandler;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.RandomAccessFile;
import java.io.DataInputStream;
import static com.sun.akuma.CLibrary.LIBC;
import com.sun.akuma.CLibrary.FILE;
/**
* List of arguments for Java VM and application.
*
* @author Kohsuke Kawaguchi
*/
public class JavaVMArguments extends ArrayList {
public JavaVMArguments() {
}
public JavaVMArguments(Collection extends String> c) {
super(c);
}
public void removeSystemProperty(String name) {
name = "-D"+name;
String nameeq = name+'=';
for (Iterator itr = this.iterator(); itr.hasNext();) {
String s = itr.next();
if(s.equals(name) || s.startsWith(nameeq))
itr.remove();
}
}
public void setSystemProperty(String name, String value) {
removeSystemProperty(name);
// index 0 is the executable name
add(1,"-D"+name+"="+value);
}
/**
* Removes the n items from the end.
* Useful for removing all the Java arguments to rebuild them.
*/
public void removeTail(int n) {
removeAll(subList(size()-n,size()));
}
/*package*/ StringArray toStringArray() {
return new StringArray(toArray(new String[size()]));
}
/**
* Gets the process argument list of the current process.
*/
public static JavaVMArguments current() throws IOException {
return of(-1);
}
/**
* Gets the process argument list of the specified process ID.
*
* @param pid
* -1 to indicate the current process.
*/
public static JavaVMArguments of(int pid) throws IOException {
String os = System.getProperty("os.name");
if("Linux".equals(os))
return ofLinux(pid);
if("SunOS".equals(os))
return ofSolaris(pid);
if("Mac OS X".equals(os))
return ofMac(pid);
throw new UnsupportedOperationException("Unsupported Operating System "+os);
}
private static JavaVMArguments ofLinux(int pid) throws IOException {
pid = resolvePID(pid);
String cmdline = readFile(new File("/proc/" + pid + "/cmdline"));
JavaVMArguments args = new JavaVMArguments(Arrays.asList(cmdline.split("\0")));
// we don't want them inherited
args.removeSystemProperty(Daemon.class.getName());
args.removeSystemProperty(NetworkServer.class.getName()+".mode");
return args;
}
private static int resolvePID(int pid) {
if(pid==-1) pid=LIBC.getpid();
return pid;
}
private static JavaVMArguments ofSolaris(int pid) throws IOException {
// /proc shows different contents based on the caller's memory model, so we need to know if we are 32 or 64.
// 32 JVMs are the norm, so err on the 32bit side.
boolean areWe64 = "64".equals(System.getProperty("sun.arch.data.model"));
pid = resolvePID(pid);
RandomAccessFile psinfo = new RandomAccessFile(new File("/proc/"+pid+"/psinfo"),"r");
try {
// see http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/sys/procfs.h
//typedef struct psinfo {
// int pr_flag; /* process flags */
// int pr_nlwp; /* number of lwps in the process */
// pid_t pr_pid; /* process id */
// pid_t pr_ppid; /* process id of parent */
// pid_t pr_pgid; /* process id of process group leader */
// pid_t pr_sid; /* session id */
// uid_t pr_uid; /* real user id */
// uid_t pr_euid; /* effective user id */
// gid_t pr_gid; /* real group id */
// gid_t pr_egid; /* effective group id */
// uintptr_t pr_addr; /* address of process */
// size_t pr_size; /* size of process image in Kbytes */
// size_t pr_rssize; /* resident set size in Kbytes */
// dev_t pr_ttydev; /* controlling tty device (or PRNODEV) */
// ushort_t pr_pctcpu; /* % of recent cpu time used by all lwps */
// ushort_t pr_pctmem; /* % of system memory used by process */
// timestruc_t pr_start; /* process start time, from the epoch */
// timestruc_t pr_time; /* cpu time for this process */
// timestruc_t pr_ctime; /* cpu time for reaped children */
// char pr_fname[PRFNSZ]; /* name of exec'ed file */
// char pr_psargs[PRARGSZ]; /* initial characters of arg list */
// int pr_wstat; /* if zombie, the wait() status */
// int pr_argc; /* initial argument count */
// uintptr_t pr_argv; /* address of initial argument vector */
// uintptr_t pr_envp; /* address of initial environment vector */
// char pr_dmodel; /* data model of the process */
// lwpsinfo_t pr_lwp; /* information for representative lwp */
//} psinfo_t;
// see http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/sys/types.h
// for the size of the various datatype.
// see http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/ptools/pargs/pargs.c
// for how to read this information
psinfo.seek(8);
if(adjust(psinfo.readInt())!=pid)
throw new IOException("psinfo PID mismatch"); // sanity check
/* The following program computes the offset:
#include
#include
int main() {
printf("psinfo_t = %d\n", sizeof(psinfo_t));
psinfo_t *x;
x = 0;
printf("%x\n", &(x->pr_argc));
}
*/
psinfo.seek(areWe64?0xEC:0xBC); // now jump to pr_argc
int argc = adjust(psinfo.readInt());
long argp = areWe64?adjust(psinfo.readLong()):to64(adjust(psinfo.readInt()));
if(LOGGER.isLoggable(FINEST))
LOGGER.finest(String.format("argc=%d,argp=%X",argc,argp));
File asFile = new File("/proc/" + pid + "/as");
if (areWe64) {
// 32bit and 64bit basically does the same thing, but because the stream position
// is computed with signed long, doing 64bit seek to a position bigger than Long.MAX_VALUE
// requres some real hacking. Hence two different code path.
// (RandomAccessFile uses Java long for offset, so it just can't get to anywhere beyond Long.MAX_VALUE)
FILE fp = LIBC.fopen(asFile.getPath(),"r");
try {
JavaVMArguments args = new JavaVMArguments();
Memory m = new Memory(8);
for( int n=0; n>8) & 0x0000FF00) | (i>>>24);
else
return i;
}
private static long adjust(long i) {
if(IS_LITTLE_ENDIAN)
return (i<<56)
| ((i<<40) & 0x00FF000000000000L)
| ((i<<24) & 0x0000FF0000000000L)
| ((i<< 8) & 0x000000FF00000000L)
| ((i>> 8) & 0x00000000FF000000L)
| ((i>>24) & 0x0000000000FF0000L)
| ((i>>40) & 0x000000000000FF00L)
| (i>>56);
else
return i;
}
/**
* int to long conversion with zero-padding.
*/
private static long to64(int i) {
return i&0xFFFFFFFFL;
}
private static String readLine(RandomAccessFile as, int p, String prefix) throws IOException {
if(LOGGER.isLoggable(FINEST))
LOGGER.finest(String.format("Reading %s at %X",prefix,p));
as.seek(to64(p));
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int ch,i=0;
while((ch=as.read())>0) {
if((++i)%100==0 && LOGGER.isLoggable(FINEST))
LOGGER.finest(prefix +" is so far "+buf.toString());
buf.write(ch);
}
String line = buf.toString();
if(LOGGER.isLoggable(FINEST))
LOGGER.finest(prefix+" was "+line);
return line;
}
private static String readLine(FILE as, long p, String prefix) throws IOException {
if(LOGGER.isLoggable(FINEST))
LOGGER.finest(String.format("Reading %s at %X",prefix,p));
seek64(as,p);
Memory m = new Memory(1);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int i=0;
while(true) {
if(LIBC.fread(m,1,1,as)==0) break;
byte b = m.getByte(0);
if(b==0) break;
if((++i)%100==0 && LOGGER.isLoggable(FINEST))
LOGGER.finest(prefix +" is so far "+buf.toString());
buf.write(b);
}
String line = buf.toString();
if(LOGGER.isLoggable(FINEST))
LOGGER.finest(prefix+" was "+line);
return line;
}
/**
* Reads the entire file.
*/
private static String readFile(File f) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileInputStream fin = new FileInputStream(f);
try {
int sz;
byte[] buf = new byte[1024];
while((sz=fin.read(buf))>=0) {
baos.write(buf,0,sz);
}
return baos.toString();
} finally {
fin.close();
}
}
/**
* Mac support
*
* See http://developer.apple.com/qa/qa2001/qa1123.html
* http://www.osxfaq.com/man/3/kvm_getprocs.ws
* http://matburt.net/?p=16 (libkvm is removed from OSX)
* where is kinfo_proc? http://lists.apple.com/archives/xcode-users/2008/Mar/msg00781.html
*
* This code uses sysctl to get the arg/env list:
* http://www.psychofx.com/psi/trac/browser/psi/trunk/src/arch/macosx/macosx_process.c
* which came from
* http://www.opensource.apple.com/darwinsource/10.4.2/top-15/libtop.c
*
* sysctl is defined in libc.
*
* PS source code for Mac:
* http://www.opensource.apple.com/darwinsource/10.4.1/adv_cmds-79.1/ps.tproj/
*/
private static JavaVMArguments ofMac(int pid) {
// local constants
final int CTL_KERN = 1;
final int KERN_ARGMAX = 8;
final int KERN_PROCARGS2 = 49;
final int sizeOfInt = Native.getNativeSize(int.class);
IntByReference _ = new IntByReference();
IntByReference argmaxRef = new IntByReference(0);
IntByReference size = new IntByReference(sizeOfInt);
// for some reason, I was never able to get sysctlbyname work.
// if(LIBC.sysctlbyname("kern.argmax", argmaxRef.getPointer(), size, NULL, _)!=0)
if(LIBC.sysctl(new int[]{CTL_KERN,KERN_ARGMAX},2, argmaxRef.getPointer(), size, NULL, _)!=0)
throw new UnsupportedOperationException("Failed to get kernl.argmax: "+LIBC.strerror(Native.getLastError()));
int argmax = argmaxRef.getValue();
LOGGER.fine("argmax="+argmax);
class StringArrayMemory extends Memory {
private long offset=0;
StringArrayMemory(long l) {
super(l);
}
int readInt() {
int r = getInt(offset);
offset+=sizeOfInt;
return r;
}
byte peek() {
return getByte(offset);
}
String readString() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte ch;
while((ch = getByte(offset++))!='\0')
baos.write(ch);
return baos.toString();
}
void skip0() {
// skip trailing '\0's
while(getByte(offset)=='\0')
offset++;
}
}
StringArrayMemory m = new StringArrayMemory(argmax);
size.setValue(argmax);
if(LIBC.sysctl(new int[]{CTL_KERN,KERN_PROCARGS2,resolvePID(pid)},3, m, size, NULL, _)!=0)
throw new UnsupportedOperationException("Failed to obtain ken.procargs2: "+LIBC.strerror(Native.getLastError()));
/*
* Make a sysctl() call to get the raw argument space of the
* process. The layout is documented in start.s, which is part
* of the Csu project. In summary, it looks like:
*
* /---------------\ 0x00000000
* : :
* : :
* |---------------|
* | argc |
* |---------------|
* | arg[0] |
* |---------------|
* : :
* : :
* |---------------|
* | arg[argc - 1] |
* |---------------|
* | 0 |
* |---------------|
* | env[0] |
* |---------------|
* : :
* : :
* |---------------|
* | env[n] |
* |---------------|
* | 0 |
* |---------------| <-- Beginning of data returned by sysctl()
* | exec_path | is here.
* |:::::::::::::::|
* | |
* | String area. |
* | |
* |---------------| <-- Top of stack.
* : :
* : :
* \---------------/ 0xffffffff
*/
JavaVMArguments args = new JavaVMArguments();
int nargs = m.readInt();
m.readString(); // exec path
for( int i=0; i lst = new ArrayList();
// while(m.peek()!=0)
// lst.add(m.readString());
return args;
}
private static final boolean IS_LITTLE_ENDIAN = "little".equals(System.getProperty("sun.cpu.endian"));
private static final Logger LOGGER = Logger.getLogger(JavaVMArguments.class.getName());
public static void main(String[] args) throws IOException {
// dump the process model of the caller
System.out.println("sun.arch.data.model="+System.getProperty("sun.arch.data.model"));
System.out.println("sun.cpu.endian="+System.getProperty("sun.cpu.endian"));
LOGGER.setLevel(Level.ALL);
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
LOGGER.addHandler(handler);
if (args.length==0)
System.out.println(current());
else {
for (String arg : args) {
System.out.println(of(Integer.valueOf(arg)));
}
}
}
}
akuma-akuma-1.9/src/main/java/com/sun/akuma/NetworkServer.java 0000664 0000000 0000000 00000021250 12077566702 0024371 0 ustar 00root root 0000000 0000000 /*
* The MIT License
*
* Copyright (c) 2009-, Sun Microsystems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sun.akuma;
import com.sun.jna.StringArray;
import java.io.FileDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketImpl;
import java.util.logging.Logger;
import java.util.List;
import java.util.Collections;
import java.util.Arrays;
import static com.sun.akuma.CLibrary.LIBC;
import sun.misc.Signal;
import sun.misc.SignalHandler;
/**
* Multi-process network server that accepts connections on the same TCP port.
*
*
* This class lets you write a Unix-like multi-process network daemon. The first process acts
* as the frontend. This creates a new socket, then fork several worker processes, which inherits
* this socket.
*
*
* Worker threads will all accept connections on this port, so even when one of the worker processes
* die off, your clients won't notice that there's a problem.
*
*
* The user of this class needs to override this class and implement abstract methods.
* Several protected methods can be also overridden to customize the behaviors.
* See {@link EchoServer} source code as an example.
*
*
* This class also inherits from {@link Daemon} to support the daemonization.
*
*
* From your main method, call into {@link #run()} method. Depending on whether the current process
* is started as a front end or a worker process, the run method behave accordingly.
*
* @author Kohsuke Kawaguchi
*/
public abstract class NetworkServer extends Daemon {
/**
* Java arguments.
*/
protected final List arguments;
protected NetworkServer(String[] args) {
this.arguments = Collections.unmodifiableList(Arrays.asList(args));
}
/**
* Entry point. Should be called from your main method.
*/
public void run() throws Exception {
String mode = System.getProperty(MODE_PROPERTY);
if("worker".equals(mode)) {
// worker process
worker();
} else {
// to run the frontend in the foreground
if(isDaemonized()) {
// running as a daemon
init();
} else {
// running in the foreground
if(shouldBeDaemonized()) {
// to launch the whole thing into a daemon
daemonize();
System.exit(0);
}
}
frontend();
}
}
/**
* Determine if we should daemonize ourselves.
*/
protected boolean shouldBeDaemonized() {
return !arguments.isEmpty() && arguments.get(0).equals("daemonize");
}
/**
* Front-end.
*/
protected void frontend() throws Exception {
ServerSocket ss = createServerSocket();
int fdn = getUnixFileDescriptor(ss);
LOGGER.fine("Listening to port "+ss.getLocalPort()+" (fd="+fdn+")");
// prepare the parameters for the exec.
JavaVMArguments forkArgs = JavaVMArguments.current();
forkArgs.setSystemProperty(NetworkServer.class.getName()+".port",String.valueOf(fdn));
forkWorkers(forkArgs);
}
/**
* Forks the worker thread with the given JVM args.
*
* The implementation is expected to modify the arguments to suit their need,
* then call into {@link #forkWorkerThreads(JavaVMArguments, int)}.
*/
protected abstract void forkWorkers(JavaVMArguments args) throws Exception;
/**
* Called by the front-end code to fork a number of worker processes into the background.
*
* This method never returns.
*/
protected void forkWorkerThreads(JavaVMArguments arguments, int n) throws Exception {
String exe = Daemon.getCurrentExecutable();
arguments.setSystemProperty(MODE_PROPERTY,"worker"); // the forked process should run as workers
LOGGER.fine("Forking worker: "+arguments);
StringArray sa = arguments.toStringArray();
// fork several worker processes
for( int i=0; i< n; i++ ) {
int r = LIBC.fork();
if(r<0) {
LIBC.perror("forking a worker process failed");
System.exit(-1);
}
if(r==0) {
// newly created child will exec to itself to get the proper Java environment back
LIBC.execv(exe,sa);
System.err.println("exec failed");
LIBC.perror("initial exec failed");
System.exit(-1);
}
}
// when we are killed, kill all the worker processes, too.
Signal.handle(new Signal("TERM"),
new SignalHandler() {
public void handle(Signal sig) {
LIBC.kill(0,SIGTERM);
System.exit(-1);
}
});
// hang forever
Object o = new Object();
synchronized (o) {
o.wait();
}
}
/**
* Creates a bound {@link ServerSocket} that will be shared by all worker processes.
* This method is called in the frontend process.
*/
protected abstract ServerSocket createServerSocket() throws Exception;
/**
* Determines the Unix file descriptor number of the given {@link ServerSocket}.
*/
private int getUnixFileDescriptor(ServerSocket ss) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Field $impl = ss.getClass().getDeclaredField("impl");
$impl.setAccessible(true);
SocketImpl socketImpl = (SocketImpl)$impl.get(ss);
Method $getFileDescriptor = SocketImpl.class.getDeclaredMethod("getFileDescriptor");
$getFileDescriptor.setAccessible(true);
FileDescriptor fd = (FileDescriptor) $getFileDescriptor.invoke(socketImpl);
Field $fd = fd.getClass().getDeclaredField("fd");
$fd.setAccessible(true);
return (Integer)$fd.get(fd);
}
protected void worker() throws Exception {
String port = System.getProperty(NetworkServer.class.getName() + ".port");
worker(recreateServerSocket(Integer.parseInt(port)));
}
/**
* Worker thread main code.
*
* @param ss
* The server socket that the frontend process created.
*/
protected abstract void worker(ServerSocket ss) throws Exception;
/**
* Recreates a bound {@link ServerSocket} on the given file descriptor.
*/
private ServerSocket recreateServerSocket(int fdn) throws Exception {
// create a properly populated FileDescriptor
FileDescriptor fd = new FileDescriptor();
Field $fd = FileDescriptor.class.getDeclaredField("fd");
$fd.setAccessible(true);
$fd.set(fd,fdn);
// now create a PlainSocketImpl
Class $PlainSocketImpl = Class.forName("java.net.PlainSocketImpl");
Constructor $init = $PlainSocketImpl.getDeclaredConstructor(FileDescriptor.class);
$init.setAccessible(true);
SocketImpl socketImpl = (SocketImpl)$init.newInstance(fd);
// then wrap that into ServerSocket
ServerSocket ss = new ServerSocket();
ss.bind(new InetSocketAddress(0));
Field $impl = ServerSocket.class.getDeclaredField("impl");
$impl.setAccessible(true);
$impl.set(ss,socketImpl);
return ss;
}
private static final Logger LOGGER = Logger.getLogger(NetworkServer.class.getName());
private static final int SIGTERM = 15;
private static final String MODE_PROPERTY = NetworkServer.class.getName() + ".mode";
}
akuma-akuma-1.9/src/site/ 0000775 0000000 0000000 00000000000 12077566702 0015324 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/src/site/apt/ 0000775 0000000 0000000 00000000000 12077566702 0016110 5 ustar 00root root 0000000 0000000 akuma-akuma-1.9/src/site/apt/index.apt 0000664 0000000 0000000 00000010747 12077566702 0017736 0 ustar 00root root 0000000 0000000 What is this?
This is a Java library you can use in your application to support Unix daemonization. By
taking advantages of POSIX API, this library lets you fork your process into background
with proper daemonization steps.
It currently has several features, which we'll see in more details.
Daemonization
One feature of this library lets you fork a copy into background as a real Unix daemon,
when started in the foreground. While doing this, you can also tweak JVM parameters or arguments.
Unlike the implementation based on <<>>, this version correctly starts a new process group,
changes the current directory, and closes file descriptors.
* How do I use it?
Your integrate akuma into your main method like this:
------------------------------------------------
public static void main(String[] args) {
Daemonizer d = new Daemonizer();
if(d.isDaemonized()) {
// perform initialization as a daemon
// this involves in closing file descriptors, recording PIDs, etc.
d.init();
} else {
// if you are already daemonized, no point in daemonizing yourself again,
// so do this only when you aren't daemonizing.
if(you decide to launch a copy into background) {
d.daemonize(...);
System.exit(0);
}
}
// your normal main code follows
// this part can be executed in two ways
// 1) the user runs your process in the foreground
// 2) you decided to daemonize yourself, in which case the newly forked daemon will execute this code,
// while the originally executed foreground Java process exits before it gets here.
...
}
------------------------------------------------
JVM Re-launch
Another feature of this library lets you re-launch a JVM with different VM options, without forking a new
child process. This allows applications to tweak parameters that can be only changed at the JVM start-up time,
such as heap size, diagnostic options, etc.
This overwrites the current process via POSIX <<>> call, so you'll retain
the same stdin/stdout/stderr, and the same parent process. In comparison, Starting yourself with <<>>
will create a child process with different stdin/stdout, and when the parent is killed, the child is left behind
without a controlling terminal.
* How do I use it?
------------------------------------------------
JavaVMArguments args = JavaVMArguments.current();
// tweak JVM launch options
Daemon.selfExec(args);
------------------------------------------------
Multi-process network server
This library also lets you write a multi-process network server that listens on the same TCP/IP port.
By forking multiple processes, you improve the robustness of your server --- a single destroyed process
will not interrupt the service as other worker processes will take over the processing.
This is how Unix daemons have been traditionally written, such as Apache, yet it was impossible to do this
in Java because it doesn't provide an API to let file descriptors to be inherited into children.
* How do I use it?
Your integrate akuma into your main method like this:
-----------------------------------------------
public class EchoServer extends NetworkServer {
public static void main(String[] args) throws Exception {
new EchoServer(args).run();
}
public EchoServer(String[] args) {
super(args);
}
@Override
protected void forkWorkers(JavaVMArguments args) throws Exception {
// fork two worker processes
forkWorkerThreads(args, 2);
}
@Override
protected ServerSocket createServerSocket() throws Exception {
// listen on port 12345
return new ServerSocket(12345);
}
@Override
protected void worker(ServerSocket ss) throws Exception {
byte[] buf = new byte[1024];
// run a simple echo server
while(true) {
Socket s = ss.accept();
... serve this socket ...
}
}
}
-----------------------------------------------
{{{https://akuma.dev.java.net/nonav/xref/com/sun/akuma/EchoServer.html}The complete example}}
is avalable separately.
Compatibility
Because of the difficulty in Java and POSIX API to obtain the OS-level arguments of the process,
not all POSIX-compliant operating systems are supported.
The supported platforms are:
* Linux (x86,amd64)
* Solaris (x86,amd64,sparc,sparcv9)
* Mac OS X
[]
akuma-akuma-1.9/src/site/site.xml 0000664 0000000 0000000 00000001626 12077566702 0017017 0 ustar 00root root 0000000 0000000
${project.name}
http://${project.artifactId}.kohsuke.org/
org.kohsuke
maven-skin
1.2
${reports}