command) {
65 | super(command);
66 | }
67 |
68 | /**
69 | * Constructor.
70 | *
71 | * @param command command
72 | */
73 | public PowerShellBuilder(String... command) {
74 | super(command);
75 | }
76 |
77 | @Override
78 | public P start(ForkerProcessListener listener) throws IOException {
79 | List a = command();
80 | if(a.size() > 0) {
81 | a.add(0, "-Command");
82 | }
83 | return super.start(listener);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/DefaultProcessFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl;
17 |
18 | import java.io.IOException;
19 |
20 | import com.sshtools.forker.client.ForkerBuilder;
21 | import com.sshtools.forker.client.ForkerProcess;
22 | import com.sshtools.forker.client.ForkerProcessFactory;
23 | import com.sshtools.forker.client.ForkerProcessListener;
24 | import com.sshtools.forker.client.NonBlockingProcessListener;
25 | import com.sshtools.forker.common.IO;
26 |
27 | /**
28 | * Create a {@link LocalProcess} if requested to explicity do so.
29 | *
30 | */
31 | public class DefaultProcessFactory implements ForkerProcessFactory {
32 |
33 | @Override
34 | public ForkerProcess createProcess(ForkerBuilder builder, ForkerProcessListener listener) throws IOException {
35 | if (builder.io() == IO.DEFAULT) {
36 | if(listener instanceof NonBlockingProcessListener) {
37 | throw new IllegalArgumentException(String.format("%s is not supported by %s, is your I/O mode set correctly (see %s.io(%s))", listener.getClass(), getClass(), ForkerBuilder.class, IO.class));
38 | }
39 | return new LocalProcess(builder);
40 | }
41 | return null;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/LocalProcessFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl;
17 |
18 | import java.io.IOException;
19 |
20 | import com.sshtools.forker.client.ForkerBuilder;
21 | import com.sshtools.forker.client.ForkerProcess;
22 | import com.sshtools.forker.client.ForkerProcessFactory;
23 | import com.sshtools.forker.client.ForkerProcessListener;
24 |
25 | /**
26 | * Creates {@link LocalProcess} as a fallback. Usually this factory comes last
27 | * in the list.
28 | */
29 | public class LocalProcessFactory implements ForkerProcessFactory {
30 |
31 | @Override
32 | public ForkerProcess createProcess(ForkerBuilder builder, ForkerProcessListener listener) throws IOException {
33 | // Finally always fallback to a standard local process
34 | LocalProcess localProcess = new LocalProcess(builder);
35 | return localProcess;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/POpenProcessFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl;
17 |
18 | import java.io.IOException;
19 |
20 | import com.sshtools.forker.client.ForkerBuilder;
21 | import com.sshtools.forker.client.ForkerProcess;
22 | import com.sshtools.forker.client.ForkerProcessFactory;
23 | import com.sshtools.forker.client.ForkerProcessListener;
24 | import com.sshtools.forker.client.NonBlockingProcessListener;
25 | import com.sshtools.forker.common.IO;
26 | import com.sshtools.forker.common.OS;
27 |
28 | /**
29 | * Creates {@link POpenProcess}.
30 | */
31 | public class POpenProcessFactory implements ForkerProcessFactory {
32 |
33 | @Override
34 | public ForkerProcess createProcess(ForkerBuilder builder, ForkerProcessListener listener) throws IOException {
35 | if (OS.isUnix() && (builder.io() == IO.INPUT || builder.io() == IO.OUTPUT)) {
36 | // We need either input, or output, but not both, so use popen
37 | if(listener instanceof NonBlockingProcessListener) {
38 | throw new IllegalArgumentException(String.format("%s is not supported by %s, is your I/O mode set correctly (see %s.io(%s))", listener.getClass(), getClass(), ForkerBuilder.class, IO.class));
39 | }
40 | return new POpenProcess(builder);
41 | }
42 | return null;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/SystemProcess.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl;
17 |
18 | import java.io.InputStream;
19 | import java.io.OutputStream;
20 |
21 | import com.sshtools.forker.client.AbstractOSProcess;
22 | import com.sshtools.forker.client.ForkerBuilder;
23 | import com.sshtools.forker.common.CSystem;
24 |
25 | /**
26 | * Uses the C call system(command). This method doesn't support any I/O
27 | * streams, and internally may still cause a fork.
28 | */
29 | public class SystemProcess extends AbstractOSProcess {
30 |
31 | private Thread thread;
32 | private int exitValue;
33 |
34 | /**
35 | * Constructor
36 | *
37 | * @param builder
38 | * builder
39 | */
40 | public SystemProcess(final ForkerBuilder builder) {
41 | thread = new Thread("SystemProcess" + builder.command()) {
42 | public void run() {
43 | exitValue = CSystem.INSTANCE.system(buildCommand(builder));
44 | };
45 | };
46 | thread.start();
47 | }
48 |
49 | @Override
50 | public OutputStream getOutputStream() {
51 | throw new UnsupportedOperationException();
52 | }
53 |
54 | @Override
55 | public InputStream getInputStream() {
56 | throw new UnsupportedOperationException();
57 | }
58 |
59 | @Override
60 | public InputStream getErrorStream() {
61 | throw new UnsupportedOperationException();
62 | }
63 |
64 | @Override
65 | public int waitFor() throws InterruptedException {
66 | thread.join();
67 | return exitValue;
68 | }
69 |
70 | @Override
71 | public int exitValue() {
72 | return exitValue;
73 | }
74 |
75 | @Override
76 | public void destroy() {
77 | throw new UnsupportedOperationException();
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/SystemProcessFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl;
17 |
18 | import java.io.IOException;
19 |
20 | import com.sshtools.forker.client.ForkerBuilder;
21 | import com.sshtools.forker.client.ForkerProcess;
22 | import com.sshtools.forker.client.ForkerProcessFactory;
23 | import com.sshtools.forker.client.ForkerProcessListener;
24 | import com.sshtools.forker.client.NonBlockingProcessListener;
25 | import com.sshtools.forker.common.IO;
26 | import com.sshtools.forker.common.OS;
27 |
28 | /**
29 | * Creates a {@link SystemProcess}.
30 | *
31 | */
32 | public class SystemProcessFactory implements ForkerProcessFactory {
33 |
34 | @Override
35 | public ForkerProcess createProcess(ForkerBuilder builder, ForkerProcessListener listener) throws IOException {
36 | if (OS.isUnix() && builder.io() == IO.SINK) {
37 |
38 | if(listener instanceof NonBlockingProcessListener) {
39 | throw new IllegalArgumentException(String.format("%s is not supported by %s, is your I/O mode set correctly (see %s.io(%s))", listener.getClass(), getClass(), ForkerBuilder.class, IO.class));
40 | }
41 | /*
42 | * We don't need any input or output, so can just start using
43 | * 'system' call which just blocks
44 | */
45 | return new SystemProcess(builder);
46 | }
47 | return null;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/jna/posix/LibEpoll.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl.jna.posix;
17 |
18 | import com.sun.jna.Native;
19 | import com.sun.jna.Platform;
20 | import com.sun.jna.Pointer;
21 |
22 | /**
23 | * @author Brett Wooldridge
24 | */
25 | public class LibEpoll {
26 | static {
27 | Native.register(Platform.C_LIBRARY_NAME);
28 | }
29 |
30 | /**
31 | * @param signal
32 | * @return status
33 | */
34 | public static native int sigignore(int signal);
35 |
36 | /**
37 | * @param size
38 | * @return status
39 | */
40 | public static native int epoll_create(int size);
41 |
42 | /**
43 | * @param epfd
44 | * @param op
45 | * @param fd
46 | * @param event
47 | * @return status
48 | */
49 | public static native int epoll_ctl(int epfd, int op, int fd, Pointer event);
50 |
51 | /**
52 | * We only ever call this API with maxevents=1. However, if calling with
53 | * maxevents > 1, care must be taken to ensure that the "events" Pointer
54 | * actually points to a contiguous block of memory large enough to handle
55 | * maxevents number of EpollEvent mappings.
56 | *
57 | * EpollEvent would likely need to be updated to add a convenience method
58 | * that allocates a block of memory and returns an array of EpollEvents
59 | * mapped into it. The EpollEvent.getPointer() of the first array element
60 | * could then be passed to this API.
61 | *
62 | * @param epfd epfd
63 | * @param events events
64 | * @param maxevents max events
65 | * @param timeout
66 | * @return status
67 | */
68 | public static native int epoll_wait(int epfd, Pointer events, int maxevents, int timeout);
69 |
70 | /**
71 | *
72 | */
73 | public static final int SIGPIPE = 13;
74 | /**
75 | * Add a file descriptor to the interface. from /usr/include/sys/epoll.h
76 | */
77 | public static final int EPOLL_CTL_ADD = 1;
78 | /**
79 | * Remove a file descriptor from the interface.
80 | */
81 | public static final int EPOLL_CTL_DEL = 2;
82 | /**
83 | * Change file descriptor epoll_event structure.
84 | */
85 | public static final int EPOLL_CTL_MOD = 3;
86 | /**
87 | *
88 | */
89 | public static final int EPOLLIN = 0x001;
90 | /**
91 | *
92 | */
93 | public static final int EPOLLOUT = 0x004;
94 | /**
95 | *
96 | */
97 | public static final int EPOLLERR = 0x008;
98 | /**
99 | *
100 | */
101 | public static final int EPOLLHUP = 0x010;
102 | /**
103 | *
104 | */
105 | public static final int EPOLLRDHUP = 0x2000;
106 | /**
107 | *
108 | */
109 | public static final int EPOLLONESHOT = (1 << 30);
110 | }
111 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/jna/posix/LibJava10.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl.jna.posix;
17 |
18 | import java.util.HashMap;
19 | import java.util.Map;
20 |
21 | import com.sshtools.forker.client.impl.nonblocking.NonBlockingBasePosixProcess;
22 | import com.sun.jna.JNIEnv;
23 | import com.sun.jna.Library;
24 | import com.sun.jna.Native;
25 | import com.sun.jna.NativeLibrary;
26 | import com.sun.jna.Platform;
27 |
28 | /**
29 | */
30 | public class LibJava10 {
31 | static {
32 | Map options = new HashMap<>();
33 | options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE);
34 | if (Platform.isMac()) {
35 | Native.register(NativeLibrary.getProcess(options));
36 | } else {
37 | Native.register(NativeLibrary.getInstance("java", options));
38 | }
39 | Java_java_lang_ProcessImpl_init(JNIEnv.CURRENT, NonBlockingBasePosixProcess.class);
40 | }
41 |
42 | /**
43 | * @param jniEnv
44 | * @param clazz
45 | */
46 | public static native void Java_java_lang_ProcessImpl_init(JNIEnv jniEnv, Object clazz);
47 |
48 | /**
49 | * JNIEXPORT jint JNICALL Java_java_lang_ProcessImpl_forkAndExec(JNIEnv
50 | * *env, jobject process, jint mode, jbyteArray helperpath, jbyteArray prog,
51 | * jbyteArray argBlock, jint argc, jbyteArray envBlock, jint envc,
52 | * jbyteArray dir, jintArray std_fds, jboolean redirectErrorStream)
53 | *
54 | * @param jniEnv
55 | * @param process
56 | * @param mode
57 | * @param helperpath
58 | * @param prog
59 | * @param argBlock
60 | * @param argc
61 | * @param envBlock
62 | * @param envc
63 | * @param dir
64 | * @param fds
65 | * @param redirectErrorStream
66 | * @return the PID of the process
67 | */
68 | public static native int Java_java_lang_ProcessImpl_forkAndExec(JNIEnv jniEnv, Object process, int mode, Object helperpath,
69 | Object prog, Object argBlock, int argc, Object envBlock, int envc, Object dir, Object fds, byte redirectErrorStream);
70 | }
71 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/jna/posix/LibJava8.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl.jna.posix;
17 |
18 | import java.util.HashMap;
19 | import java.util.Map;
20 |
21 | import com.sshtools.forker.client.impl.nonblocking.NonBlockingBasePosixProcess;
22 | import com.sun.jna.JNIEnv;
23 | import com.sun.jna.Library;
24 | import com.sun.jna.Native;
25 | import com.sun.jna.NativeLibrary;
26 | import com.sun.jna.Platform;
27 |
28 | /**
29 | */
30 | public class LibJava8 {
31 | static {
32 | Map options = new HashMap<>();
33 | options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE);
34 | if (Platform.isMac()) {
35 | Native.register(NativeLibrary.getProcess(options));
36 | } else {
37 | Native.register(NativeLibrary.getInstance("java", options));
38 | }
39 | Java_java_lang_UNIXProcess_init(JNIEnv.CURRENT, NonBlockingBasePosixProcess.class);
40 | }
41 |
42 | /**
43 | * @param jniEnv
44 | * @param clazz
45 | */
46 | public static native void Java_java_lang_UNIXProcess_init(JNIEnv jniEnv, Object clazz);
47 |
48 | /**
49 | * JNIEXPORT jint JNICALL Java_java_lang_UNIXProcess_forkAndExec(JNIEnv
50 | * *env, jobject process, jint mode, jbyteArray helperpath, jbyteArray prog,
51 | * jbyteArray argBlock, jint argc, jbyteArray envBlock, jint envc,
52 | * jbyteArray dir, jintArray std_fds, jboolean redirectErrorStream)
53 | *
54 | * @param jniEnv
55 | * @param process
56 | * @param mode
57 | * @param helperpath
58 | * @param prog
59 | * @param argBlock
60 | * @param argc
61 | * @param envBlock
62 | * @param envc
63 | * @param dir
64 | * @param fds
65 | * @param redirectErrorStream
66 | *
67 | * @return the PID of the process
68 | */
69 | public static native int Java_java_lang_UNIXProcess_forkAndExec(JNIEnv jniEnv, Object process, int mode, Object helperpath,
70 | Object prog, Object argBlock, int argc, Object envBlock, int envc, Object dir, Object fds, byte redirectErrorStream);
71 | }
72 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/jna/win32/WindowsAuthenticationTokens.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl.jna.win32;
17 |
18 | import com.sun.jna.platform.win32.WinNT.HANDLE;
19 |
20 | /**
21 | * TODO
22 | */
23 | public class WindowsAuthenticationTokens {
24 |
25 | HANDLE hProfile;
26 | HANDLE hToken;
27 | }
28 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/nonblocking/ReferenceCountedFileDescriptor.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.impl.nonblocking;
17 |
18 | import java.io.Closeable;
19 |
20 | import com.sshtools.forker.client.impl.jna.posix.LibC;
21 | import com.sun.jna.LastErrorException;
22 |
23 | /**
24 | * Encapsulates a file descriptor plus a reference count to ensure close
25 | * requests only close the file descriptor once the last reference to the file
26 | * descriptor is released.
27 | *
28 | * If not explicitly closed, the file descriptor will be closed when this object
29 | * is finalized.
30 | */
31 | public class ReferenceCountedFileDescriptor implements Closeable {
32 | private int fd;
33 | private int fdRefCount;
34 | private boolean closePending;
35 |
36 | /**
37 | * Constructor
38 | *
39 | * @param fd file description
40 | */
41 | public ReferenceCountedFileDescriptor(int fd) {
42 | this.fd = fd;
43 | this.fdRefCount = 0;
44 | this.closePending = false;
45 | }
46 |
47 | protected void finalize() {
48 | close();
49 | }
50 |
51 | /**
52 | * Acquire a reference.
53 | *
54 | * @return file description
55 | */
56 | public synchronized int acquire() {
57 | fdRefCount++;
58 | return fd;
59 | }
60 |
61 | /**
62 | * Release a reference.
63 | */
64 | public synchronized void release() {
65 | fdRefCount--;
66 | if (fdRefCount == 0 && closePending && fd != -1) {
67 | doClose();
68 | }
69 | }
70 |
71 | public synchronized void close() {
72 | if (fd == -1 || closePending) {
73 | return;
74 | }
75 | if (fdRefCount == 0) {
76 | doClose();
77 | } else {
78 | // Another thread has the FD. We'll close it when they release the
79 | // reference.
80 | closePending = true;
81 | }
82 | }
83 |
84 | private void doClose() {
85 | try {
86 | LibC.close(fd);
87 | fd = -1;
88 | } catch (LastErrorException e) {
89 | throw new RuntimeException(e);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/impl/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * This packages contains the implementations of the processes
18 | * {@link com.sshtools.forker.client.ForkerBuilder} creates.
19 | *
20 | * There would usually be no need to use the classes directly.
21 | */
22 | package com.sshtools.forker.client.impl;
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * This packages contains the main classes that will be used in client code
18 | * making use of the Forker framework.
19 | *
20 | * The three classes you are most likely to want to use include :-
21 | *
22 | * - {@link com.sshtools.forker.client.ForkerBuilder} - which is an API
23 | * compatible replacement for {@link java.lang.ProcessBuilder}.
24 | * - {@link com.sshtools.forker.client.OSCommand} - which contains a set of
25 | * static helper methods to make running processes easy, taking care of checking
26 | * exit codes and redirecting I/O
27 | * - {@link com.sshtools.forker.client.ShellBuilder} - which is a
28 | * specialisation of ForkerBuilder and can be used to create an interactive
29 | * shell (often used with the Forker Pty module).
30 | *
31 | */
32 | package com.sshtools.forker.client;
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/ui/AskPassConsole.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.client.ui;
17 |
18 | import java.io.BufferedReader;
19 | import java.io.Console;
20 | import java.io.InputStreamReader;
21 |
22 | /**
23 | * Simple console based helper application that asks for a password (input on
24 | * stdin, message on stderr) and prints it on stdout.
25 | */
26 | public class AskPassConsole {
27 |
28 | /**
29 | * Entry point.
30 | *
31 | * @param args
32 | * command line arguments
33 | * @throws Exception
34 | * on any error
35 | */
36 | public static void main(String[] args) throws Exception {
37 | Console console = System.console();
38 | if (console == null)
39 | System.err.println("WARNING: Not on a console, password will be visible");
40 |
41 | // Title
42 | String title = System.getenv("ASKPASS_TITLE");
43 | if (title == null) {
44 | title = "Administrator Password Required";
45 | }
46 |
47 | // Text
48 | String text = System.getenv("ASKPASS_TEXT");
49 | if (text == null) {
50 | text = "This application requires elevated privileges. Please\n";
51 | text += "enter the administrator password to continue.";
52 | }
53 |
54 | System.err.println(title);
55 | System.err.println();
56 | System.err.println(text);
57 | System.err.println();
58 |
59 | System.err.print("Enter a password:");
60 | String pw = null;
61 | if (console == null) {
62 | BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
63 | pw = br.readLine();
64 | } else {
65 | char[] c = console.readPassword("");
66 | pw = c == null ? null : new String(c);
67 | }
68 | if (pw != null)
69 | System.out.println(pw);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/forker-client/src/main/java/com/sshtools/forker/client/ui/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * This packages contains some helper applications such as 'Ask Pass' programs
18 | * and launchers that may be used by some operating system commands for
19 | * privilege escalation and user switching.
20 | *
21 | * There would usually be no need to use the classes directly.
22 | */
23 | package com.sshtools.forker.client.ui;
--------------------------------------------------------------------------------
/forker-client/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Common module
3 | *
4 | * @uses com.sshtools.forker.client.ForkerProcessFactory
5 | */
6 | module com.sshtools.forker.client {
7 | exports com.sshtools.forker.client;
8 | exports com.sshtools.forker.client.impl;
9 | exports com.sshtools.forker.client.impl.nonblocking;
10 | exports com.sshtools.forker.client.impl.jna.posix;
11 | exports com.sshtools.forker.client.impl.jna.win32;
12 | exports com.sshtools.forker.client.impl.jna.osx;
13 | exports com.sshtools.forker.client.ui;
14 |
15 | requires transitive com.sshtools.forker.common;
16 | requires static java.desktop;
17 | requires java.logging;
18 |
19 | uses com.sshtools.forker.client.ForkerProcessFactory;
20 | }
--------------------------------------------------------------------------------
/forker-common/.gitignore:
--------------------------------------------------------------------------------
1 | /.settings/
2 | /target/
3 | *.classpath
4 | /bin/
5 |
--------------------------------------------------------------------------------
/forker-common/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | forker-common
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.wst.common.project.facet.core.builder
10 |
11 |
12 |
13 |
14 | org.eclipse.jdt.core.javabuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.validation.validationbuilder
20 |
21 |
22 |
23 |
24 | org.eclipse.m2e.core.maven2Builder
25 |
26 |
27 |
28 |
29 |
30 | org.eclipse.jem.workbench.JavaEMFNature
31 | org.eclipse.wst.common.modulecore.ModuleCoreNature
32 | org.eclipse.jdt.core.javanature
33 | org.eclipse.m2e.core.maven2Nature
34 | org.eclipse.wst.common.project.facet.core.nature
35 |
36 |
37 |
--------------------------------------------------------------------------------
/forker-common/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | forker-common
6 | Forker Common
7 | Common classes for the forker system.
8 |
9 | com.sshtools
10 | forker
11 | 1.8
12 | ..
13 |
14 |
15 |
16 | src/main/java
17 | src/test/java
18 | target/classes
19 | target/test-classes
20 |
21 |
22 | .
23 | src/main/resources
24 |
25 |
26 |
27 |
28 | .
29 | src/test/resources
30 |
31 |
32 |
33 |
34 |
35 |
40 |
41 | net.java.dev.jna
42 | jna
43 | 5.9.0
44 |
45 |
46 | net.java.dev.jna
47 | jna-platform
48 | 5.9.0
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/forker-common/src/main/java/com/sshtools/forker/common/Priority.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.common;
17 |
18 | /**
19 | * Represents the priority of a process. Note, the OS may support more less
20 | * priorities, but currently a least common denominator approach has been taken
21 | * with this feature. This may change in the future.
22 | *
23 | */
24 | public enum Priority {
25 | /**
26 | * Low priority
27 | */
28 | LOW,
29 | /**
30 | * Normal priority, i.e. as decided by OS when priority is not explicitly
31 | * set
32 | */
33 | NORMAL,
34 | /**
35 | * High priority
36 | */
37 | HIGH,
38 | /**
39 | * Realtime (when supported)
40 | */
41 | REALTIME
42 | }
43 |
--------------------------------------------------------------------------------
/forker-common/src/main/java/com/sshtools/forker/common/States.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.common;
17 |
18 | /**
19 | * Various constants used by the Forker Daemon protocol.
20 | *
21 | */
22 | public class States {
23 |
24 | /**
25 | * Remote command or operation executed OK
26 | */
27 | public final static int OK = 0;
28 | /**
29 | * Remote command or operation failed to execute OK
30 | */
31 | public final static int FAILED = 1;
32 | /**
33 | * Apply operation to input stream
34 | */
35 | public final static int IN = 2;
36 | /**
37 | * Apply operation to output stream
38 | */
39 | public final static int ERR = 3;
40 | /**
41 | * End of stream
42 | */
43 | public final static int END = 4;
44 | /**
45 | * Apply operation to output stream
46 | */
47 | public final static int OUT = 5;
48 | /**
49 | * Kill process
50 | */
51 | public final static int KILL = 6;
52 | /**
53 | * Close output stream
54 | */
55 | public final static int CLOSE_OUT = 7;
56 | /**
57 | * Close error stream
58 | */
59 | public final static int CLOSE_ERR = 8;
60 | /**
61 | * Close input stream
62 | */
63 | public final static int CLOSE_IN = 9;
64 | /**
65 | * Flush output stream
66 | */
67 | public final static int FLUSH_OUT = 10;
68 | /**
69 | * Window size changed (either direction)
70 | */
71 | public final static int WINDOW_SIZE = 11;
72 | }
73 |
--------------------------------------------------------------------------------
/forker-common/src/main/java/com/sshtools/forker/common/XKernel32.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.common;
2 |
3 | import com.sun.jna.Native;
4 | import com.sun.jna.platform.win32.Kernel32;
5 | import com.sun.jna.win32.W32APIOptions;
6 |
7 | public interface XKernel32 extends Kernel32 {
8 | /**
9 | * Instance
10 | */
11 | XKernel32 INSTANCE = Native.load("Kernel32", XKernel32.class, W32APIOptions.UNICODE_OPTIONS);
12 |
13 | int SetCurrentDirectoryW(String path);
14 | }
15 |
--------------------------------------------------------------------------------
/forker-common/src/main/java/com/sshtools/forker/common/XWinsvc.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.common;
2 |
3 | import com.sun.jna.Pointer;
4 | import com.sun.jna.Structure;
5 | import com.sun.jna.Structure.FieldOrder;
6 | import com.sun.jna.platform.win32.WinDef.DWORD;
7 | import com.sun.jna.platform.win32.Winsvc;
8 | import com.sun.jna.win32.W32APITypeMapper;
9 |
10 | public interface XWinsvc extends Winsvc {
11 |
12 | @FieldOrder({ "fDelayedAutostart" })
13 | public class SERVICE_DELAYED_AUTO_START_INFO extends ChangeServiceConfig2Info {
14 | public static class ByReference extends SERVICE_DELAYED_AUTO_START_INFO implements Structure.ByReference {
15 | }
16 |
17 | public boolean fDelayedAutostart;
18 | }
19 |
20 | public final static DWORD SERVICE_SID_TYPE_NONE = new DWORD(0x00000000);
21 | public final static DWORD SERVICE_SID_TYPE_RESTRICTED = new DWORD(0x00000003);
22 | public final static DWORD SERVICE_SID_TYPE_UNRESTRICTED = new DWORD(0x00000001);
23 |
24 | @FieldOrder({ "dwServiceSidType" })
25 | public class SERVICE_SID_INFO extends ChangeServiceConfig2Info {
26 | public static class ByReference extends SERVICE_SID_INFO implements Structure.ByReference {
27 | }
28 |
29 | public DWORD dwServiceSidType;
30 | }
31 |
32 | @FieldOrder({ "lpDescription" })
33 | public class SERVICE_DESCRIPTION extends ChangeServiceConfig2Info {
34 | public static class ByReference extends SERVICE_DESCRIPTION implements Structure.ByReference {
35 | }
36 |
37 | public String lpDescription;
38 | }
39 |
40 | @FieldOrder({ "lpServiceName", "lpDisplayName", "ServiceStatusProcess" })
41 | public static class ENUM_SERVICE_STATUS_PROCESS extends Structure {
42 | public Pointer lpServiceName;
43 | public Pointer lpDisplayName;
44 | public SERVICE_STATUS_PROCESS ServiceStatusProcess;
45 |
46 | public ENUM_SERVICE_STATUS_PROCESS() {
47 | super(W32APITypeMapper.DEFAULT);
48 | }
49 |
50 | public ENUM_SERVICE_STATUS_PROCESS(Pointer pointer) {
51 | super(pointer);
52 | read();
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/forker-common/src/main/java/com/sshtools/forker/common/package-info.java:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Contains the Forker Daemon, used to run processes from a separate
4 | * (smaller JVM) and provide other features, such as the PTY plugin that
5 | * provides a way to launch real interactive shells.
6 | *
7 | * Unless you are extending Forker, for most cases, you do not have to worry
8 | * about either directly running or configuring the daemon, it is all handled
9 | * more or less automatically.
10 | */
11 | package com.sshtools.forker.common;
--------------------------------------------------------------------------------
/forker-common/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Common module
3 | * @uses com.sshtools.forker.common.IO
4 | */
5 | module com.sshtools.forker.common {
6 | requires transitive com.sun.jna;
7 | requires transitive com.sun.jna.platform;
8 | exports com.sshtools.forker.common;
9 | uses com.sshtools.forker.common.IO;
10 | }
--------------------------------------------------------------------------------
/forker-examples/.gitignore:
--------------------------------------------------------------------------------
1 | /.settings/
2 | /target/
3 | *.classpath
4 | /bin/
5 | *.cfg
6 | /tmp/
7 |
--------------------------------------------------------------------------------
/forker-examples/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | forker-examples
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.m2e.core.maven2Builder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.m2e.core.maven2Nature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/forker-examples/app.args:
--------------------------------------------------------------------------------
1 | -classpath
2 | /home/tanktarta/.m2/repository/org/jetbrains/pty4j/purejavacomm/0.0.11.1/purejavacomm-0.0.11.1.jar:/home/tanktarta/.m2/repository/org/jetbrains/annotations/16.0.2/annotations-16.0.2.jar:/home/tanktarta/.m2/repository/com/google/guava/guava/25.1-jre/guava-25.1-jre.jar:/home/tanktarta/.m2/repository/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar:/home/tanktarta/.m2/repository/org/checkerframework/checker-qual/2.0.0/checker-qual-2.0.0.jar:/home/tanktarta/.m2/repository/com/google/errorprone/error_prone_annotations/2.1.3/error_prone_annotations-2.1.3.jar:/home/tanktarta/.m2/repository/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar:/home/tanktarta/.m2/repository/org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar:/home/tanktarta/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:/apps/eclipse-2020-12-M2/configuration/org.eclipse.osgi/1793/0/.cp/lib/javaagent-shaded.jar:/home/tanktarta/.m2/repository/org/jetbrains/pty4j/purejavacomm/0.0.11.1/purejavacomm-0.0.11.1.jar:/home/tanktarta/.m2/repository/org/jetbrains/annotations/16.0.2/annotations-16.0.2.jar:/home/tanktarta/.m2/repository/com/google/guava/guava/25.1-jre/guava-25.1-jre.jar:/home/tanktarta/.m2/repository/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar:/home/tanktarta/.m2/repository/org/checkerframework/checker-qual/2.0.0/checker-qual-2.0.0.jar:/home/tanktarta/.m2/repository/com/google/errorprone/error_prone_annotations/2.1.3/error_prone_annotations-2.1.3.jar:/home/tanktarta/.m2/repository/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar:/home/tanktarta/.m2/repository/org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar:/home/tanktarta/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:/apps/eclipse-2020-12-M2/configuration/org.eclipse.osgi/1793/0/.cp/lib/javaagent-shaded.jar
3 | -p
4 | /home/tanktarta/Documents/Git/forker-develop/forker-examples/target/classes:/home/tanktarta/Documents/Git/forker-develop/forker-client/target/classes:/home/tanktarta/Documents/Git/forker-develop/forker-common/target/classes:/home/tanktarta/.m2/repository/org/apache/commons/commons-lang3/3.11/commons-lang3-3.11.jar:/home/tanktarta/Documents/Git/forker-develop/forker-pty/target/classes:/home/tanktarta/.m2/repository/net/java/dev/jna/jna/5.6.0/jna-5.6.0.jar:/home/tanktarta/.m2/repository/net/java/dev/jna/jna-platform/5.6.0/jna-platform-5.6.0.jar:/home/tanktarta/.m2/repository/org/jetbrains/pty4j/pty4j/0.9.8/pty4j-0.9.8.jar:/home/tanktarta/Documents/Git/forker-develop/forker-wrapper/target/classes:/home/tanktarta/.m2/repository/info/picocli/picocli/4.6.1/picocli-4.6.1.jar:/home/tanktarta/Documents/Git/forker-develop/forker-wrapped/target/classes
5 | -Dforker.info.attempts=1
6 | --add-modules
7 | com.sshtools.forker.examples
8 |
--------------------------------------------------------------------------------
/forker-examples/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | forker-examples
4 | Forker Examples
5 | Examples for the forker system.
6 |
7 | com.sshtools
8 | forker
9 | 1.8
10 | ..
11 |
12 |
13 |
14 | src/main/java
15 | src/test/java
16 | target/classes
17 | target/test-classes
18 |
19 |
20 | .
21 | src/main/resources
22 |
23 |
24 |
25 |
26 | .
27 | src/test/resources
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | ${project.groupId}
37 | forker-client
38 | ${project.version}
39 |
40 |
41 | ${project.groupId}
42 | forker-pty
43 | ${project.version}
44 |
45 |
46 | ${project.groupId}
47 | forker-wrapper
48 | ${project.version}
49 |
50 |
51 | ${project.groupId}
52 | forker-wrapped
53 | ${project.version}
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/AdministratorElevate.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import com.sshtools.forker.client.EffectiveUser;
4 | import com.sshtools.forker.client.EffectiveUserFactory;
5 | import com.sshtools.forker.client.ForkerBuilder;
6 | import com.sshtools.forker.common.IO;
7 | import com.sshtools.forker.common.Util;
8 | import com.sun.jna.Platform;
9 |
10 | /**
11 | * Demonstrates elevating to administrator using {@link ForkerBuilder}.
12 | */
13 | public class AdministratorElevate {
14 | public static void main(String[] args) throws Exception {
15 |
16 | /* Get the user object for admnistrator */
17 | EffectiveUser administrator = EffectiveUserFactory.getDefault().administrator();
18 |
19 | /* Create the builder */
20 | ForkerBuilder builder = new ForkerBuilder().effectiveUser(administrator).io(IO.IO);
21 | if (Platform.isLinux()) {
22 | /* The linux example tries to list the shadow password file */
23 | builder.redirectErrorStream(true).io(IO.IO).command("cat", "/etc/shadow");
24 | } else if (Platform.isWindows()) {
25 | /*
26 | * Windows try to create a filename protected by WRP
27 | * builder.command("dir", ">", "c:\\forker-test.exe");
28 | * builder.command("C:\\windows\\system32\\cmd.exe", "/c", "dir
29 | * \\");
30 | */
31 | builder.command("C:\\windows\\system32\\xcopy.exe");
32 | builder.io(IO.SINK);
33 | } else {
34 | throw new UnsupportedOperationException();
35 | }
36 |
37 | Process p = builder.start();
38 | Util.copy(p.getInputStream(), System.out);
39 | System.out.println(" (" + p.waitFor() + ")");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/ForkerWrapperHelper.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import com.sshtools.forker.wrapper.ForkerWrapper;
4 |
5 | /**
6 | * This is just here to help with Eclipse launching a mixed JPMS/non-JPMS
7 | * project. It may be deleted in the future, is not actually an example,
8 | * and has no other use.
9 | */
10 | public class ForkerWrapperHelper extends ForkerWrapper {
11 |
12 | public static void main(String[] args) {
13 | ForkerWrapper.main(args);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/NativeForkWrappedTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | /** This is the application that {@link NativeForkWrapperTest} will launch */
4 | public class NativeForkWrappedTest {
5 |
6 | public NativeForkWrappedTest() {
7 | }
8 |
9 | public static void main(String[] args) throws Exception {
10 | System.out.println("Running as " + System.getProperty("user.name"));
11 |
12 | for (int i = 0; i < 1000; i++) {
13 | System.out.println("Running " + i);
14 | Thread.sleep(1000);
15 | }
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/NativeForkWrapperTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import com.sshtools.forker.wrapper.ForkerWrapper;
4 |
5 | /**
6 | * Shows you can embed {@link ForkerWrapper}.
7 | */
8 | public class NativeForkWrapperTest {
9 |
10 | public static void main(String[] args) throws Exception {
11 | // OSCommand.restartAsAdministrator(NativeForkWrapperTest.class.getPackageName(), NativeForkWrapperTest.class, args);
12 |
13 | System.out.println("Running as " + System.getProperty("user.name"));
14 | ForkerWrapper fw = new ForkerWrapper();
15 |
16 | // fw.setProperty("quiet", true);
17 | // fw.setProperty("level", "SEVERE");
18 |
19 | fw.getWrappedApplication().setModule(NativeForkWrappedTest.class.getPackageName());
20 | fw.getWrappedApplication().setClassname(NativeForkWrappedTest.class.getName());
21 | fw.getConfiguration().setRemaining("arg1");
22 | fw.getConfiguration().setProperty("level", "FINE");
23 | fw.getConfiguration().setProperty("native-fork", "true");
24 | fw.getConfiguration().setProperty("daemon", "true");
25 |
26 | // Start and wait for wrapper to exit
27 | System.out.println("Wrapped process returned: " + fw.start());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/NonBlocking.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | import com.sshtools.forker.client.DefaultNonBlockingProcessListener;
6 | import com.sshtools.forker.client.ForkerBuilder;
7 | import com.sshtools.forker.client.NonBlockingProcess;
8 | import com.sshtools.forker.common.IO;
9 | import com.sshtools.forker.common.OS;
10 |
11 | /**
12 | * Simple non-blocking I/O example that reads the output of a command.
13 | */
14 | public class NonBlocking {
15 | public static void main(String[] args) throws Exception {
16 | ForkerBuilder builder = new ForkerBuilder().io(IO.NON_BLOCKING).redirectErrorStream(true);
17 | if (OS.isUnix()) {
18 | // The unix example tries to list the root directory
19 | builder.command("ls", "-al", "/");
20 | } else {
21 | builder.command("DIR", "C:\\");
22 | }
23 | NonBlockingProcess process = new ForkerBuilder("ls", "-al", "/").io(IO.NON_BLOCKING).redirectErrorStream(true)
24 | .start(new DefaultNonBlockingProcessListener() {
25 | @Override
26 | public void onStdout(NonBlockingProcess process, ByteBuffer buffer, boolean closed) {
27 | if (!closed) {
28 | byte[] bytes = new byte[buffer.remaining()];
29 | /* Consume bytes from buffer (so position is updated) */
30 | buffer.get(bytes);
31 | System.out.println(new String(bytes));
32 | }
33 | }
34 | });
35 | /*
36 | * Not strictly required, this is just to hold up the example thread until the
37 | * command is finished, your use case may or may not need to wait for the
38 | * command to finish.
39 | */
40 | System.out.println("Done: " + process.waitFor());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/NonBlockingShell.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.examples;
17 |
18 | import java.nio.ByteBuffer;
19 |
20 | import com.sshtools.forker.client.DefaultNonBlockingProcessListener;
21 | import com.sshtools.forker.client.NonBlockingProcess;
22 | import com.sshtools.forker.client.ShellBuilder;
23 | import com.sshtools.forker.common.IO;
24 | import com.sshtools.forker.common.OS;
25 | import com.sshtools.forker.common.Util;
26 |
27 | /**
28 | * This example shows how to create an interactive shell.
29 | *
30 | */
31 | public class NonBlockingShell {
32 |
33 | public static void main(String[] args) throws Exception {
34 | /*
35 | * This example reads from stdin (i.e. the console), so stdin needs to be unbuffered with
36 | * no local echoing at this end of the pipe, the following function
37 | * attempts to do this.
38 | */
39 | OS.unbufferedStdin();
40 |
41 | /* ShellBuilder is a specialisation of ForkerBuilder */
42 | ShellBuilder shell = new ShellBuilder();
43 | shell.io(IO.NON_BLOCKING);
44 | shell.redirectErrorStream(true);
45 |
46 | /* Demonstrate we are actually in a different shell by setting PS1 */
47 | shell.environment().put("MYENV", "An environment variable");
48 |
49 | final NonBlockingProcess p = shell.start(new DefaultNonBlockingProcessListener() {
50 | @Override
51 | public void onStdout(NonBlockingProcess process, ByteBuffer buffer, boolean closed) {
52 | if (!closed) {
53 | byte[] bytes = new byte[buffer.remaining()];
54 | /* Consume bytes from buffer (so position is updated) */
55 | buffer.get(bytes);
56 | System.out.println(new String(bytes));
57 | }
58 | }
59 | });
60 |
61 | /* While using non-blocking, it is still convenient in this case to use
62 | * the OutputStream provided by the process as we are just joining
63 | * it to stdin of this process
64 | */
65 | Util.copy(System.in, p.getOutputStream());
66 |
67 | /* When this processes stdin closes, close the shells stdin too and
68 | * wait for it to finish
69 | */
70 | p.getOutputStream().close();
71 | int ret = p.waitFor();
72 | System.err.println("Exited with code: " + ret);
73 | System.exit(ret);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/OSCommandElevate.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import static com.sshtools.forker.client.OSCommand.admin;
4 | import static com.sshtools.forker.client.OSCommand.elevate;
5 | import static com.sshtools.forker.client.OSCommand.restrict;
6 | import static com.sshtools.forker.client.OSCommand.run;
7 |
8 | /**
9 | * Demonstrates using elevated commands
10 | *
11 | */
12 | public class OSCommandElevate {
13 |
14 | public static void main(String[] args) throws Exception {
15 | /*
16 | * First run an admin command, each time such a command is run
17 | * the password will be required. The 'admin' method is a shortcut for elevating
18 | * permissions then using 'run'
19 | */
20 |
21 | // The shorthand version
22 | admin("ifconfig");
23 |
24 | // The longhand version
25 | elevate();
26 | try {
27 | run("ifconfig");
28 | } finally {
29 | restrict();
30 | }
31 |
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/PowerShellCommand.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import com.sshtools.forker.client.PowerShellBuilder;
4 | import com.sshtools.forker.common.Util;
5 |
6 | /**
7 | * Demonstrates running a PowerShell command using {@link PowerShellBuilder}.
8 | */
9 | public class PowerShellCommand {
10 | public static void main(String[] args) throws Exception {
11 | /* Create the builder */
12 | Process p = new PowerShellBuilder("$PSVersionTable").start();
13 | Util.copy(p.getInputStream(), System.out);
14 | System.out.println(" (" + p.waitFor() + ")");
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/RestartAsAdministrator.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import static com.sshtools.forker.client.OSCommand.run;
4 |
5 | import com.sshtools.forker.client.OSCommand;
6 | import com.sshtools.forker.common.OS;
7 |
8 | /**
9 | * Restart this application as an administrator.
10 | */
11 | public class RestartAsAdministrator {
12 | public static void main(String[] args) throws Exception {
13 | OSCommand.restartAsAdministrator(RestartAsAdministrator.class, args);
14 | if (OS.isUnix()) {
15 | run("cat", "/etc/passwd");
16 | run("id");
17 | }
18 | else {
19 | String pf = System.getenv("PROGRAMFILES");
20 | if (pf == null)
21 | pf = "C:\\Program Files";
22 | run("MKDIR", pf + "\\SimpleElevate.TMP");
23 | run("RD", pf + "\\SimpleElevate.TMP");
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/RunAsUser.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import com.sshtools.forker.client.EffectiveUser;
4 | import com.sshtools.forker.client.EffectiveUserFactory;
5 | import com.sshtools.forker.client.ForkerBuilder;
6 | import com.sshtools.forker.common.IO;
7 | import com.sshtools.forker.common.Util;
8 | import com.sun.jna.Platform;
9 |
10 | /**
11 | * Demonstrates running a command as another user.
12 | */
13 | public class RunAsUser {
14 |
15 | public static void main(String[] args) throws Exception {
16 |
17 | /* Either supply a username on the command line when you run this class or
18 | * change this username 'testuser2' to one that exists on your system.
19 | */
20 | String username = args.length == 0 ? "testuser2" : args[0];
21 |
22 | /* Get the user object for this user */
23 | EffectiveUser user = EffectiveUserFactory.getDefault().getUserForUsername(username);
24 |
25 | /* Create the builder */
26 | ForkerBuilder builder = new ForkerBuilder().effectiveUser(user).
27 | io(IO.IO).redirectErrorStream(true);
28 |
29 | if(Platform.isLinux()) {
30 | // The linux example tries to list the users home directory
31 | builder.command("id");
32 | }
33 | else {
34 | throw new UnsupportedOperationException();
35 | }
36 |
37 | Process p = builder.start();
38 | Util.copy(p.getInputStream(), System.out);
39 | System.out.println(" (" + p.waitFor() + ")");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/Shell.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import java.io.IOException;
4 |
5 | import com.sshtools.forker.client.ShellBuilder;
6 | import com.sshtools.forker.common.OS;
7 | import com.sshtools.forker.common.Util;
8 | import com.sshtools.forker.pty.PTYProcess;
9 | import com.sshtools.forker.pty.PTYProcess.PTYProcessListener;
10 |
11 | /**
12 | * This example shows how to create an interactive shell.
13 | *
14 | */
15 | public class Shell {
16 |
17 | public static void main(String[] args) throws Exception {
18 | /*
19 | * This example reads from stdin (i.e. the console), so stdin needs to be unbuffered with
20 | * no local echoing at this end of the pipe, the following function
21 | * attempts to do this.
22 | */
23 | OS.unbufferedStdin();
24 |
25 |
26 | /* ShellBuilder is a specialisation of ForkerBuilder */
27 | ShellBuilder shell = new ShellBuilder();
28 | shell.io(PTYProcess.PTY);
29 |
30 | /* Demonstrate we are actually in a different shell by setting PS1 */
31 | shell.environment().put("MYENV", "An environment variable");
32 |
33 | /* Start the shell, giving it a window size listener */
34 | final Process p = shell.start(new PTYProcessListener() {
35 | @Override
36 | public void windowSizeChanged(int ptyWidth, int ptyHeight) {
37 | System.out.println("Window size changed to " + ptyWidth + " x " + ptyHeight);
38 | }
39 | });
40 |
41 |
42 | new Thread() {
43 | public void run() {
44 | try {
45 | Util.copy(System.in, p.getOutputStream());
46 | } catch (IOException e) {
47 | } finally {
48 | // Close the process input stream when stdin closes, this
49 | // will end the process
50 | try {
51 | p.getOutputStream().close();
52 | } catch (IOException e) {
53 | }
54 | }
55 | }
56 | }.start();
57 | Util.copy(p.getInputStream(), System.out);
58 | int ret = p.waitFor();
59 | System.err.println("Exited with code: " + ret);
60 | System.exit(ret);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/ShellAsUser.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import java.io.Console;
4 | import java.io.IOException;
5 |
6 | import com.sshtools.forker.client.EffectiveUserFactory.DefaultEffectiveUserFactory;
7 | import com.sshtools.forker.client.ForkerBuilder;
8 | import com.sshtools.forker.client.ShellBuilder;
9 | import com.sshtools.forker.common.OS;
10 | import com.sshtools.forker.common.Util;
11 | import com.sshtools.forker.pty.PTYProcess;
12 |
13 | /**
14 | * Launch an interactive login shell as a particular user using a Pseudo
15 | * Terminal, or PTY. PTY supports is currently only available via Forker
16 | * Daemon, which in this example is launched as an administrator allowing
17 | * any user to be used for the shell.
18 | */
19 | public class ShellAsUser {
20 |
21 | public static void main(String[] args) throws Exception {
22 | /*
23 | * This example reads from stdni, so stdid needs to be unbuffered with
24 | * no local echoing at this end of the pipe, the following function
25 | * attempts to do this
26 | */
27 | OS.unbufferedStdin();
28 |
29 | ForkerBuilder shell = new ShellBuilder().loginShell(true).io(PTYProcess.PTY).redirectErrorStream(true);
30 |
31 | /*
32 | * Run the shell as the user that launches this class. Any valid UID
33 | * could be used, but we just get the current UID (using 'id -u'
34 | * command) and ask for the shell we spawn to use the that user.
35 | */
36 | Console console = System.console();
37 | if (console == null && args.length == 0)
38 | throw new IOException("No console available and no username supplied as command line argument.");
39 | shell.effectiveUser(DefaultEffectiveUserFactory.getDefault()
40 | .getUserForUsername(args.length == 0 ? console.readLine("Username:") : args[0]));
41 |
42 | // Start process
43 | final Process p = shell.start();
44 |
45 | /*
46 | * Connect both the input and the output streams, start the process and
47 | * wait for it to finish
48 | */
49 | new Thread() {
50 | public void run() {
51 | try {
52 | Util.copy(System.in, p.getOutputStream());
53 | } catch (IOException e) {
54 | } finally {
55 | // Close the process input stream when stdin closes, this
56 | // will end the process
57 | try {
58 | p.getOutputStream().close();
59 | } catch (IOException e) {
60 | }
61 | }
62 | }
63 | }.start();
64 | Util.copy(p.getInputStream(), System.out);
65 | int ret = p.waitFor();
66 | System.err.println("Exited with code: " + ret);
67 | System.exit(ret);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/SimpleElevate.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import static com.sshtools.forker.client.OSCommand.elevate;
4 | import static com.sshtools.forker.client.OSCommand.restrict;
5 | import static com.sshtools.forker.client.OSCommand.run;
6 |
7 | import com.sshtools.forker.client.OSCommand;
8 | import com.sshtools.forker.common.OS;
9 | import com.sun.jna.Platform;
10 |
11 | /**
12 | * Show uses {@link OSCommand}, that uses {@link ThreadLocal} state to configure
13 | * and run simple commands.
14 | */
15 | public class SimpleElevate {
16 | public static void main(String[] args) throws Exception {
17 | elevate();
18 | try {
19 | if (Platform.isMac())
20 | run("cat", "/etc/master.passwd");
21 | else if (OS.isUnix())
22 | run("cat", "/etc/passwd");
23 | else {
24 | String pf = System.getenv("PROGRAMFILES");
25 | if (pf == null)
26 | pf = "C:\\Program Files";
27 | run("MKDIR", pf + "\\SimpleElevate.TMP");
28 | run("RD", pf + "\\SimpleElevate.TMP");
29 | }
30 | } finally {
31 | restrict();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/WrappedProcessTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import com.sshtools.forker.client.ForkerBuilder;
4 | import com.sshtools.forker.common.OS;
5 | import com.sshtools.forker.common.Util;
6 | import com.sshtools.forker.wrapper.ForkerWrapper;
7 | import com.sshtools.forker.wrapper.KeyValuePair;
8 | import com.sshtools.forker.wrapper.WrapperIO;
9 | import com.sshtools.forker.wrapper.WrapperProcessFactory;
10 | import com.sun.jna.Platform;
11 |
12 | /**
13 | * Another way of embedded {@link ForkerWrapper}, this time by using the
14 | * {@link ForkerBuilder} with an I/O mode of {@link WrapperIO}.
15 | */
16 | public class WrappedProcessTest {
17 |
18 | public static void main(String[] args) throws Exception {
19 | // Standard builder creation
20 | ForkerBuilder fb = new ForkerBuilder();
21 |
22 | // Choose a command to run based on OS
23 | if (OS.isUnix())
24 | fb.parse("ls /etc");
25 | else if (Platform.isWindows())
26 | fb.parse("DIR C:\\");
27 | else
28 | throw new UnsupportedOperationException("Add a command for your OS to test.");
29 |
30 | // Get a handle on the process factory, allowing configuration of it
31 | WrapperProcessFactory processFactory = fb.configuration().processFactory(WrapperProcessFactory.class);
32 |
33 | processFactory.addOption(new KeyValuePair("native", "true"));
34 | processFactory.addOption(new KeyValuePair("level", "FINEST"));
35 | processFactory.addOption(new KeyValuePair("restart-on", "0"));
36 | processFactory.addOption(new KeyValuePair("restart-wait", "10"));
37 |
38 | // Launches a new JVM
39 | processFactory.setSeparateProcess(true);
40 |
41 | // Tell forker builder we want a wrapped process
42 | fb.io(WrapperIO.WRAPPER);
43 |
44 | // Boilerplate stuff
45 | fb.redirectErrorStream(true);
46 | Process p = fb.start();
47 | Util.copy(p.getInputStream(), System.out);
48 | System.out.println(" (" + p.waitFor() + ")");
49 |
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/WrappedTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | /** This is the application that {@link WrapperTest} will launch */
4 | public class WrappedTest {
5 |
6 | public WrappedTest() {
7 | }
8 |
9 | public static void main(String[] args) throws Exception {
10 | Runtime.getRuntime().addShutdownHook(new Thread() {
11 | @Override
12 | public void run() {
13 | try {
14 | for (int i = 0; i < 20; i++) {
15 | System.out.println("Waiting to exit " + i + "/" + 20);
16 | Thread.sleep(1000);
17 | }
18 | } catch (Exception e) {
19 | }
20 | }
21 | });
22 |
23 | for (int i = 0; i < 1000; i++) {
24 | System.out.println("Running " + i);
25 | Thread.sleep(1000);
26 | }
27 |
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/WrapperJavascriptTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import java.io.File;
4 |
5 | import com.sshtools.forker.wrapper.ForkerWrapper;
6 |
7 | /**
8 | * Shows you can embed {@link ForkerWrapper} and use a JavaScript
9 | * configuration file to configure it.
10 | */
11 | public class WrapperJavascriptTest {
12 |
13 | public static void main(String[] args) throws Exception {
14 | ForkerWrapper fw = new ForkerWrapper();
15 | fw.getConfiguration().setRemaining(args);
16 | fw.readConfigFile(new File("wrapper-javascript-test.cfg.js"));
17 |
18 | // Start and wait for wrapper to exit
19 | System.out.println("Wrapped process returned: " + fw.start());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/com/sshtools/forker/examples/WrapperTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.examples;
2 |
3 | import com.sshtools.forker.wrapper.ForkerWrapper;
4 |
5 | /**
6 | * Shows you can embed {@link ForkerWrapper}.
7 | */
8 | public class WrapperTest {
9 |
10 | public static void main(String[] args) throws Exception {
11 | ForkerWrapper fw = new ForkerWrapper();
12 |
13 | fw.getWrappedApplication().setModule(WrappedTest.class.getPackageName());
14 | fw.getWrappedApplication().setClassname(WrappedTest.class.getName());
15 | fw.getConfiguration().setRemaining("arg1");
16 | fw.getConfiguration().setProperty("level", "INFO");
17 |
18 | // Start and wait for wrapper to exit
19 | System.out.println("Wrapped process returned: " + fw.start());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/forker-examples/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module com.sshtools.forker.examples {
2 | requires java.logging;
3 | requires com.sshtools.forker.client;
4 | requires com.sshtools.forker.pty;
5 | requires com.sshtools.forker.wrapped;
6 | requires transitive com.sshtools.forker.wrapper;
7 |
8 | exports com.sshtools.forker.examples;
9 |
10 |
11 | }
--------------------------------------------------------------------------------
/forker-examples/wrapper-javascript-test.cfg.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /* This is a test JavaScript wrapper configuration file.
17 | * You can mix code and configuration, but this script
18 | * must evaluate to (return) an object containing the configuration
19 | * properties (i.e. a map of all the same properties that might
20 | * be provided by other methods)
21 | */
22 |
23 | java.lang.System.out.println('Im configured by a script!');
24 |
25 | /* Return the configuration object. The outer brackets are required. */
26 |
27 | ({
28 | main: 'com.nervepoint.forker.examples.WrappedTest',
29 | level: 'WARNING',
30 | arg: [
31 | 'arg1',
32 | 'arg2'
33 | ]
34 | })
--------------------------------------------------------------------------------
/forker-pipes/.gitignore:
--------------------------------------------------------------------------------
1 | /.settings/
2 | /target/
3 | *.classpath
4 | /bin/
5 |
--------------------------------------------------------------------------------
/forker-pipes/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | forker-pipes
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.wst.common.project.facet.core.builder
10 |
11 |
12 |
13 |
14 | org.eclipse.jdt.core.javabuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.validation.validationbuilder
20 |
21 |
22 |
23 |
24 | org.eclipse.m2e.core.maven2Builder
25 |
26 |
27 |
28 |
29 |
30 | org.eclipse.jem.workbench.JavaEMFNature
31 | org.eclipse.wst.common.modulecore.ModuleCoreNature
32 | org.eclipse.jdt.core.javanature
33 | org.eclipse.m2e.core.maven2Nature
34 | org.eclipse.wst.common.project.facet.core.nature
35 |
36 |
37 |
--------------------------------------------------------------------------------
/forker-pipes/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | forker-pipes
6 | Forker Pipes
7 | Abstracts operating system pipes, such as unix domain sockets on Unix and friends, and
8 | named pipes on Windows.
9 |
10 | com.sshtools
11 | forker
12 | 1.8
13 | ..
14 |
15 |
16 |
17 | src/main/java
18 | src/test/java
19 | target/classes
20 | target/test-classes
21 |
22 |
23 | .
24 | src/main/resources
25 |
26 |
27 |
28 |
29 | .
30 | src/test/resources
31 |
32 |
33 |
34 |
35 |
36 |
37 | org.apache.commons
38 | commons-lang3
39 | 3.11
40 |
41 |
42 | ${project.groupId}
43 | forker-common
44 | ${project.version}
45 |
46 |
47 | net.java.dev.jna
48 | jna
49 | 5.9.0
50 |
51 |
52 | net.java.dev.jna
53 | jna-platform
54 | 5.9.0
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/forker-pipes/src/main/java/com/sshtools/forker/pipes/DefaultPipeFactory.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.pipes;
2 |
3 | import java.io.IOException;
4 | import java.net.ServerSocket;
5 | import java.net.Socket;
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | import com.sshtools.forker.common.OS;
10 | import com.sun.jna.Platform;
11 |
12 | public class DefaultPipeFactory implements PipeFactory {
13 |
14 | @Override
15 | public ServerSocket createPipeServer(String name, Flag... flags) throws IOException {
16 | ServerSocket serverSocket = serverSocket(name);
17 | serverSocket.bind(null);
18 | return serverSocket;
19 | }
20 |
21 | @Override
22 | public Socket createPipe(String name, Flag... flags) throws IOException {
23 | Socket socket = socket(name);
24 | socket.connect(null);
25 | return socket;
26 | }
27 |
28 | protected Socket socket(String name, Flag... flags) throws IOException {
29 | if (OS.isUnix())
30 | return new UnixDomainSocket(toUnixName(name, flags), flags);
31 | else if (Platform.isWindows())
32 | return new NamedPipeSocket(toWindowsName(name, flags), flags);
33 | throw new UnsupportedOperationException();
34 | }
35 |
36 | protected ServerSocket serverSocket(String name, Flag... flags) throws IOException {
37 | if (OS.isUnix())
38 | return new UnixDomainServerSocket(toUnixName(name, flags), flags);
39 | else if (Platform.isWindows())
40 | return new NamedPipeServerSocket(toWindowsName(name, flags), flags);
41 | throw new UnsupportedOperationException();
42 | }
43 |
44 | protected String validateName(String name, Flag[] flags) {
45 | for (char c : name.toCharArray()) {
46 | if (!Character.isAlphabetic(c) && !Character.isDigit(c) && c != '_' && c != '.' && c != '-' && (!Platform.isWindows() || c != '\\'))
47 | throw new IllegalArgumentException(String.format("Invalid pipe name. %s", name));
48 | }
49 | return name;
50 | }
51 |
52 | protected String toUnixName(String name, Flag... flags) {
53 | List flagsList = Arrays.asList(flags);
54 | if (flagsList.contains(Flag.CONCRETE))
55 | return "/tmp/" + validateName(name, flags);
56 | else
57 | return "\0/tmp/" + validateName(name, flags);
58 | }
59 |
60 | protected String toWindowsName(String name, Flag... flags) {
61 | List flagsList = Arrays.asList(flags);
62 | if (flagsList.contains(Flag.CONCRETE) && !flagsList.contains(Flag.ABSTRACT))
63 | throw new UnsupportedOperationException("Windows does not support concrete file names for pipes.");
64 |
65 | if (flagsList.contains(Flag.REMOTE)) {
66 | String[] parts = name.split(":");
67 | if (parts.length != 2)
68 | throw new IllegalArgumentException("For a remote pipe, the name must be in the format host:name");
69 | return "\\\\" + parts[0] + "\\pipe\\" + validateName(parts[1], flags);
70 | } else {
71 | return "\\\\.\\pipe\\" + validateName(name, flags);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/forker-pipes/src/main/java/com/sshtools/forker/pipes/PipeFactory.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.pipes;
2 |
3 | import java.io.IOException;
4 | import java.net.ServerSocket;
5 | import java.net.Socket;
6 |
7 | /**
8 | * A factory for creating pipes.
9 | */
10 | public interface PipeFactory {
11 |
12 | /**
13 | * Flags
14 | */
15 | public class Flag {
16 |
17 | /**
18 | * Use a virtual file (if supported). E.g. on unix this would be an 'abstract
19 | * path'. If the underlying OS does not support it an
20 | * {@link UnsupportedOperationException} will be thrown, unless
21 | * {@link Flag#CONCRETE} is specified and that is supported.
22 | */
23 | public final static Flag ABSTRACT = new Flag();
24 |
25 | /**
26 | * Use a concrete file (if supported). E.g. on unix this would be an 'pathname'.
27 | * If the underlying OS does not support it an
28 | * {@link UnsupportedOperationException} will be thrown, unless
29 | * {@link Flag#ABSTRACT} is specified and that is supported.
30 | */
31 | public final static Flag CONCRETE = new Flag();
32 |
33 | /**
34 | * Access a remote pipe (if supported). The name must be in the format
35 | * host:name
if this flag is set. If the underlying OS does not
36 | * support it an {@link UnsupportedOperationException} will be thrown, unless
37 | * {@link Flag#ABSTRACT} is specified and that is supported.
38 | */
39 | public final static Flag REMOTE = new Flag();
40 |
41 | /**
42 | * Wait for a pipe to becoming available before opening it. When this flag
43 | * is not present, an exception will be thrown when the pipe is created.
44 | */
45 | public final static Flag WAIT = new Flag();
46 | }
47 |
48 | /**
49 | * Creates a new pipe.
50 | *
51 | * @param name the name
52 | * @param flags flags
53 | * @return the socket
54 | * @throws IOException Signals that an I/O exception has occurred.
55 | */
56 | Socket createPipe(String name, Flag... flags) throws IOException;
57 |
58 | /**
59 | * Creates a new pipe server.
60 | *
61 | * @param name the name
62 | * @param flags flags
63 | * @return the server socket
64 | * @throws IOException Signals that an I/O exception has occurred.
65 | */
66 | ServerSocket createPipeServer(String name, Flag... flags) throws IOException;
67 | }
68 |
--------------------------------------------------------------------------------
/forker-pipes/src/main/java/com/sshtools/forker/pipes/Unix.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.pipes;
2 |
3 | import com.sshtools.forker.common.CSystem;
4 | import com.sun.jna.LastErrorException;
5 |
6 | /**
7 | * Unix utilities.
8 | */
9 | public class Unix {
10 | static String formatError(LastErrorException lee) {
11 | try {
12 | return CSystem.INSTANCE.strerror(lee.getErrorCode());
13 | } catch (Throwable t) {
14 | return lee.getMessage();
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/forker-pipes/src/main/java/com/sshtools/forker/pipes/UnixDomainServerSocket.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.pipes;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.ServerSocket;
6 | import java.net.Socket;
7 | import java.net.SocketAddress;
8 | import java.util.concurrent.atomic.AtomicBoolean;
9 |
10 | import com.sshtools.forker.common.CSystem;
11 | import com.sshtools.forker.common.CSystem.SockAddr;
12 | import com.sshtools.forker.pipes.PipeFactory.Flag;
13 | import com.sun.jna.LastErrorException;
14 | import com.sun.jna.Platform;
15 |
16 | /**
17 | * Unix domain server socket
18 | */
19 | public class UnixDomainServerSocket extends ServerSocket {
20 | private final AtomicBoolean lock = new AtomicBoolean();
21 | private final int fd;
22 | private final SockAddr address;
23 | private boolean closed;
24 |
25 | UnixDomainServerSocket(String path, Flag... flags) throws IOException {
26 | if (Platform.isWindows() || Platform.isWindowsCE()) {
27 | throw new IOException("Unix domain sockets are not supported on Windows");
28 | }
29 | File socketFile = new File(path);
30 | socketFile.delete();
31 | address = new SockAddr(path);
32 | socketFile.deleteOnExit();
33 | lock.set(false);
34 | try {
35 | fd = CSystem.INSTANCE.socket(CSystem.AF_UNIX, CSystem.SOCK_STREAM, CSystem.PROTOCOL);
36 | } catch (LastErrorException lee) {
37 | throw new IOException("native socket() failed : " + Unix.formatError(lee));
38 | }
39 | }
40 |
41 | /**
42 | * Bind.
43 | *
44 | * @param endpoint the endpoint
45 | * @param backlog the backlog
46 | * @throws IOException Signals that an I/O exception has occurred.
47 | */
48 | @Override
49 | public void bind(SocketAddress endpoint, int backlog) throws IOException {
50 | try {
51 | CSystem.INSTANCE.bind(fd, address, address.size());
52 | } catch (LastErrorException lee) {
53 | throw new IOException("native socket() failed : " + Unix.formatError(lee));
54 | }
55 | try {
56 | CSystem.INSTANCE.listen(fd, backlog);
57 | } catch (LastErrorException lee) {
58 | throw new IOException("native socket() failed : " + Unix.formatError(lee));
59 | }
60 | }
61 |
62 | /**
63 | * Accept.
64 | *
65 | * @return the socket
66 | * @throws IOException Signals that an I/O exception has occurred.
67 | */
68 | @Override
69 | public Socket accept() throws IOException {
70 | int tfd;
71 | try {
72 | tfd = CSystem.INSTANCE.accept(fd, null, 0);
73 | } catch (LastErrorException lee) {
74 | throw new IOException("native socket() failed : " + Unix.formatError(lee));
75 | }
76 |
77 | return new UnixDomainSocket(tfd);
78 | }
79 |
80 | /**
81 | * Checks if is closed.
82 | *
83 | * @return true, if is closed
84 | */
85 | @Override
86 | public boolean isClosed() {
87 | return closed;
88 | }
89 |
90 | /**
91 | * Close.
92 | *
93 | * @throws IOException Signals that an I/O exception has occurred.
94 | */
95 | @Override
96 | public void close() throws IOException {
97 | if (!lock.getAndSet(true)) {
98 | try {
99 | CSystem.INSTANCE.close(fd);
100 | } catch (LastErrorException lee) {
101 | throw new IOException("native close() failed : " + Unix.formatError(lee));
102 | } finally {
103 | closed = true;
104 | }
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/forker-pipes/src/main/java/com/sshtools/forker/pipes/package-info.java:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Abstraction of operating system pipes.
4 | */
5 | package com.sshtools.forker.pipes;
--------------------------------------------------------------------------------
/forker-pipes/src/main/java/com/sshtools/forker/pipes/package.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Abstraction of operating system pipes.
6 |
7 |
--------------------------------------------------------------------------------
/forker-pipes/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Pipes module
3 | */
4 | module com.sshtools.forker.pipes {
5 | requires com.sshtools.forker.common;
6 | exports com.sshtools.forker.pipes;
7 | }
--------------------------------------------------------------------------------
/forker-pipes/src/test/java/com/sshtools/forker/pipes/DefaultPipeFactoryTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.pipes;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 | import java.net.ServerSocket;
6 | import java.net.Socket;
7 |
8 | import org.junit.BeforeClass;
9 | import org.junit.Test;
10 |
11 | import com.sshtools.forker.pipes.PipeFactory.Flag;
12 |
13 | public class DefaultPipeFactoryTest {
14 | private static DefaultPipeFactory factory;
15 |
16 | @BeforeClass
17 | public static void createFactory() throws Exception {
18 | factory = new DefaultPipeFactory();
19 | }
20 |
21 | @Test
22 | public void singlePipe() throws Exception {
23 | String pipeName = "us_xfr";
24 | ServerSocket srvSock = factory.createPipeServer(pipeName, Flag.ABSTRACT);
25 | Thread srv = new Thread() {
26 | public void run() {
27 | System.out.println("Accepting");
28 | try (Socket socket = srvSock.accept()) {
29 | System.out.println("Transferring stream");
30 | socket.getInputStream().transferTo(System.out);
31 | System.out.println("Transferred stream");
32 | } catch (IOException ie) {
33 | ie.printStackTrace();
34 | }
35 | }
36 | };
37 | System.out.println("Starting");
38 | srv.start();
39 |
40 | System.out.println("Creating client");
41 | try (Socket pipe = factory.createPipe(pipeName, Flag.ABSTRACT)) {
42 | System.out.println("Getting output stream");
43 | try(OutputStream out = pipe.getOutputStream()) {
44 | out.write("Test".getBytes());
45 | // out.flush();
46 | }
47 | }
48 |
49 | srv.join();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/forker-pty/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | *.classpath
4 | /bin/
5 | /linux/
6 | /logs/
7 | /tmp/
8 |
--------------------------------------------------------------------------------
/forker-pty/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | forker-pty
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.wst.common.project.facet.core.builder
10 |
11 |
12 |
13 |
14 | org.eclipse.jdt.core.javabuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.validation.validationbuilder
20 |
21 |
22 |
23 |
24 | org.eclipse.m2e.core.maven2Builder
25 |
26 |
27 |
28 |
29 |
30 | org.eclipse.jem.workbench.JavaEMFNature
31 | org.eclipse.wst.common.modulecore.ModuleCoreNature
32 | org.eclipse.jdt.core.javanature
33 | org.eclipse.m2e.core.maven2Nature
34 | org.eclipse.wst.common.project.facet.core.nature
35 |
36 |
37 |
--------------------------------------------------------------------------------
/forker-pty/README.md:
--------------------------------------------------------------------------------
1 | # Forker PTY support
2 |
3 | This module adds support for a new I/O mode, [PTYExecutor.PTY](src/main/java/com/sshtools/forker/pty/PTYExecutor.java).
4 | This mode will use [Forker Daemon](../forker-daemon/README.md) to maintain one or more pseudo terminals.
5 |
6 | ## Maven
7 |
8 | This module is not available on Maven Central due to it's dependencies. Instead it can be found at our own Artifactory server :-
9 |
10 |
11 | ```xml
12 |
13 |
14 | opensource-releases
15 | http://artifactory.javassh.com/opensource-snapshots
16 | SSHTOOLS Open Source Releases
17 |
18 |
19 | ```
20 |
21 | And your dependency configuration :-
22 |
23 | ```
24 |
25 |
26 | com.sshtools
27 | forker-pty
28 | 1.5
29 |
30 |
31 | ```
32 |
33 | ## Usage
34 |
35 | Be aware that non-blocking I/O modes do not yet work with the PTY add on.
36 |
37 | ## Example
38 |
39 | ```java
40 | /*
41 | * This example reads from stdin (i.e. the console), so stdin needs to be unbuffered with
42 | * no local echoing at this end of the pipe, the following function
43 | * attempts to do this.
44 | */
45 | OS.unbufferedStdin();
46 |
47 | /* PTY requires the daemon, so load it now (or connect to an existing one if you
48 | * have started it yourself). */
49 | Forker.loadDaemon();
50 | // Forker.connectDaemon(new Instance("NOAUTH:57872"));
51 |
52 | /* ShellBuilder is a specialisation of ForkerBuilder */
53 | ShellBuilder shell = new ShellBuilder();
54 | shell.loginShell(true);
55 | shell.io(PTYExecutor.PTY);
56 | shell.redirectErrorStream(true);
57 |
58 | /* Demonstrate we are actually in a different shell by setting PS1 */
59 | shell.environment().put("PS1", ">>>");
60 |
61 | /* Start the shell, giving it a window size listener */
62 | final Process p = shell.start(new Listener() {
63 | @Override
64 | public void windowSizeChanged(int ptyWidth, int ptyHeight) {
65 | System.out.println("Window size changed to " + ptyWidth + " x " + ptyHeight);
66 | }
67 | });
68 |
69 |
70 | new Thread() {
71 | public void run() {
72 | try {
73 | IOUtils.copy(System.in, p.getOutputStream());
74 | } catch (IOException e) {
75 | } finally {
76 | // Close the process input stream when stdin closes, this
77 | // will end the process
78 | try {
79 | p.getOutputStream().close();
80 | } catch (IOException e) {
81 | }
82 | }
83 | }
84 | }.start();
85 | IOUtils.copy(p.getInputStream(), System.out);
86 | int ret = p.waitFor();
87 | System.err.println("Exited with code: " + ret);
88 | System.exit(ret);
89 | ```
90 |
--------------------------------------------------------------------------------
/forker-pty/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | forker-pty
6 | Forker PTY
7 | This module adds PTY capability to forker daemon, alllowing real interactive shells to be launched.
8 |
9 | com.sshtools
10 | forker
11 | 1.8
12 | ..
13 |
14 |
15 |
16 | src/main/java
17 | src/test/java
18 |
19 |
20 | .
21 | src/main/resources
22 |
23 |
24 |
25 |
26 |
27 |
28 | ${project.groupId}
29 | forker-client
30 | ${project.version}
31 |
32 |
33 | net.java.dev.jna
34 | jna
35 | 5.9.0
36 |
37 |
38 | net.java.dev.jna
39 | jna-platform
40 | 5.9.0
41 |
42 |
43 | org.jetbrains.pty4j
44 | pty4j
45 | 0.12.7
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | ext-releases-local
54 |
55 |
56 | false
57 |
58 | https://artifactory.jadaptive.com/ext-releases-local
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/forker-pty/src/main/java/com/sshtools/forker/pty/PTYIO.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.pty;
2 |
3 | import com.sshtools.forker.common.IO.DefaultIO;
4 |
5 | public final class PTYIO extends DefaultIO {
6 | public PTYIO() {
7 | super("PTY", true, true, true, true);
8 | }
9 | }
--------------------------------------------------------------------------------
/forker-pty/src/main/java/com/sshtools/forker/pty/PTYProcessFactory.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.pty;
2 |
3 | import java.io.IOException;
4 |
5 | import com.sshtools.forker.client.ForkerBuilder;
6 | import com.sshtools.forker.client.ForkerProcess;
7 | import com.sshtools.forker.client.ForkerProcessFactory;
8 | import com.sshtools.forker.client.ForkerProcessListener;
9 | import com.sshtools.forker.client.NonBlockingProcessListener;
10 | import com.sshtools.forker.common.IO;
11 | import com.sshtools.forker.common.OS;
12 | import com.sun.jna.Platform;
13 |
14 | /**
15 | * Creates a {@link PTYProcess}.
16 | */
17 | public class PTYProcessFactory implements ForkerProcessFactory {
18 |
19 | @Override
20 | public ForkerProcess createProcess(ForkerBuilder builder, ForkerProcessListener listener) throws IOException {
21 | if (OS.isUnix() && !Platform.isMac() && builder.io() == PTYProcess.PTY) {
22 | if(listener instanceof NonBlockingProcessListener) {
23 | throw new IllegalArgumentException(String.format("%s is not supported by %s, is your I/O mode set correctly (see %s.io(%s))", listener.getClass(), getClass(), ForkerBuilder.class, IO.class));
24 | }
25 | return new PTYProcess(builder);
26 | }
27 | return null;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/forker-pty/src/main/java/com/sshtools/forker/pty/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * Provides the classes required to support creation of Pseudo Terminals for
18 | * interactive shells and other commands.
19 | *
20 | * For example, this in conjunction with other Forker suite facilities (such as
21 | * as privilege escalation and run as user) could be used to create an SSH or
22 | * Telnet server in Java.
23 | */
24 | package com.sshtools.forker.pty;
--------------------------------------------------------------------------------
/forker-pty/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * PTY module
3 | */
4 | module com.sshtools.forker.pty {
5 | requires transitive com.sshtools.forker.common;
6 | requires transitive pty4j;
7 | requires transitive com.sshtools.forker.client;
8 | exports com.sshtools.forker.pty;
9 |
10 | provides com.sshtools.forker.common.IO
11 | with com.sshtools.forker.pty.PTYIO;
12 |
13 | provides com.sshtools.forker.client.ForkerProcessFactory
14 | with com.sshtools.forker.pty.PTYProcessFactory;
15 |
16 | }
--------------------------------------------------------------------------------
/forker-pty/src/main/resources/META-INF/services/com.sshtools.forker.client.ForkerProcessFactory:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.pty.PTYProcessFactory
--------------------------------------------------------------------------------
/forker-pty/src/main/resources/META-INF/services/com.sshtools.forker.common.IO:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.pty.PTYIO
--------------------------------------------------------------------------------
/forker-services/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | *.classpath
4 | /bin/
5 |
--------------------------------------------------------------------------------
/forker-services/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | forker-services
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.wst.common.project.facet.core.builder
10 |
11 |
12 |
13 |
14 | org.eclipse.jdt.core.javabuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.validation.validationbuilder
20 |
21 |
22 |
23 |
24 | org.eclipse.m2e.core.maven2Builder
25 |
26 |
27 |
28 |
29 |
30 | org.eclipse.jem.workbench.JavaEMFNature
31 | org.eclipse.wst.common.modulecore.ModuleCoreNature
32 | org.eclipse.jdt.core.javanature
33 | org.eclipse.m2e.core.maven2Nature
34 | org.eclipse.wst.common.project.facet.core.nature
35 |
36 |
37 |
--------------------------------------------------------------------------------
/forker-services/README.md:
--------------------------------------------------------------------------------
1 | # Forker Services
2 |
3 | ## Introduction
4 |
5 | Forker Services provides a way of starting, stopping and querying local system services. Multiple
6 | implementations are provided for the 3 main operating systems the Forker suite supportes. On
7 | Windows, the *sc* command is currently used (with polling). On Linux and Unix there are multiple
8 | options, with the most efficient being the SystemD DBus based one.
9 |
10 | ## Adding Forker Services To Your Project
11 |
12 | To include the Forker Services in your project, you will need the module :-
13 |
14 | ### Maven
15 |
16 | ```
17 |
18 |
19 | com.sshtools
20 | forker-services
21 | 1.6
22 |
23 |
24 | ```
25 |
26 | ## Usage
27 |
28 | The API is very simple. You just need to obtain an instane of a `ServiceService` and call it's various methods to control the local services.
29 |
30 | ### Quick Start
31 |
32 | For the impatient :-
33 |
34 | ```java
35 |
36 | // List services
37 | ServiceService ss = Services.get();
38 | for(Service s : ss.getServices()) {
39 | System.out.println(s.getNativeName());
40 | }
41 |
42 | // Stop a service
43 | ss.stopService(ss.getService("ntp"));
44 |
45 | // Start a service
46 | ss.startService(ss.getService("ntp"));
47 |
48 | // Listen for changes in service state
49 | ss.addListener(new ServicesListener() {
50 |
51 | void stateChanged(Service service) {
52 | // Services has started, stopped, etc
53 | }
54 |
55 | void extendedStatusChanged(Service service, ExtendedServiceStatus extStatus) {
56 | // Extended information about the state change if available, called after stateChange()
57 | }
58 |
59 | void serviceAdded(Service service) {
60 | // New service has appeared
61 | }
62 |
63 | void serviceRemoved(Service service) {
64 | // Service has disappeated
65 | }
66 | });
67 |
68 | ```
69 |
70 | ### Usage
71 |
72 | #### Obtaining ServiceService
73 |
74 | ##### Using The Services Helper
75 |
76 | The default and easiest way to initialize and obtain a `ServiceService` is to call `Services.get()`. By default, this will detected the best implementation to use for your platform.
77 |
78 | You can specify you own implementation by setting the system property `forker.services.impl` before `get()` is called.
79 |
80 | You can also call `Services.set(myServiceService)`, and again, this must be done before `get()` is called.
81 |
82 | ##### Manually
83 |
84 | If you have you own platform detecting logic, simply instantiate the appropriate implementation yourself.
85 |
86 | ```java
87 |
88 | ServiceService ss;
89 | if(getMyOS().equals("WINDOWS")) {
90 | ss = new Win32ServiceService();
91 | }
92 | else {
93 | // TODO detect other OSs
94 | }
95 |
96 | // You must then initialize with your own instance of ServicesContext
97 | ss.configure(new MyContext());
98 |
99 | ```
100 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/com/sshtools/forker/services/DefaultContext.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.services;
17 |
18 | import java.io.IOException;
19 | import java.util.concurrent.Callable;
20 | import java.util.concurrent.Executors;
21 | import java.util.concurrent.ScheduledExecutorService;
22 | import java.util.concurrent.ScheduledFuture;
23 | import java.util.concurrent.TimeUnit;
24 |
25 | public class DefaultContext implements ServicesContext {
26 | private ScheduledExecutorService executor;
27 | {
28 | executor = Executors.newScheduledThreadPool(1);
29 | }
30 |
31 | @Override
32 | public ScheduledFuture> schedule(Runnable runnable, long initialDelay, long delay, TimeUnit units) {
33 | return executor.scheduleAtFixedRate(runnable, initialDelay, delay, units);
34 | }
35 |
36 | @Override
37 | public void call(Callable> callable) {
38 | executor.schedule(callable, 0, TimeUnit.MILLISECONDS);
39 | }
40 |
41 | @Override
42 | public void close() throws IOException {
43 | executor.shutdown();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/com/sshtools/forker/services/ExtendedServiceStatus.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.services;
17 |
18 | import javax.swing.JComponent;
19 |
20 | public interface ExtendedServiceStatus {
21 | int getMessageType();
22 |
23 | String getId();
24 |
25 | String getText();
26 |
27 | Throwable getError();
28 |
29 | JComponent getAccessory();
30 | }
31 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/com/sshtools/forker/services/Service.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.services;
17 |
18 | public interface Service {
19 |
20 | public interface Listener {
21 | void extendedServiceStatusChanged(Service service, ExtendedServiceStatus newStatus);
22 | }
23 |
24 | public enum Status {
25 | STARTED, STARTING, STOPPED, STOPPING, PAUSING, PAUSED, UNPAUSING, UNKNOWN;
26 |
27 | public boolean isRunning() {
28 | return this == STARTED || this == STARTING || this == PAUSED || this ==PAUSING || this == PAUSED;
29 | }
30 | }
31 |
32 | void addListener(Listener l);
33 |
34 | void removeListener(Listener l);
35 |
36 | void configure(ServiceService service);
37 |
38 | String getNativeName();
39 |
40 | Status getStatus();
41 |
42 | ExtendedServiceStatus getExtendedStatus();
43 | }
44 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/com/sshtools/forker/services/ServiceService.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.services;
17 |
18 | import java.io.Closeable;
19 | import java.io.IOException;
20 | import java.util.List;
21 |
22 | public interface ServiceService extends Closeable {
23 | public final static String BUNDLE = "services";
24 |
25 | void configure(ServicesContext context);
26 |
27 | void addListener(ServicesListener listener);
28 |
29 | void removeListener(ServicesListener listener);
30 |
31 | List getServices() throws IOException;
32 |
33 | void restartService(Service service) throws Exception;
34 |
35 | void startService(Service service) throws Exception;
36 |
37 | void pauseService(Service service) throws Exception;
38 |
39 | void unpauseService(Service service) throws Exception;
40 |
41 | void stopService(Service service) throws Exception;
42 |
43 | void setStartOnBoot(Service service, boolean startOnBoot) throws Exception;
44 |
45 | boolean isStartOnBoot(Service service) throws Exception;
46 |
47 | Service getService(String name) throws IOException;
48 |
49 | default boolean hasService(String name) throws IOException {
50 | return getService(name) != null;
51 | }
52 |
53 | default void close() throws IOException {
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/com/sshtools/forker/services/ServiceState.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.services;
17 |
18 | public enum ServiceState {
19 | STOPPED, STARTING, STARTED, STOPPING
20 | }
21 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/com/sshtools/forker/services/ServicesContext.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.services;
17 |
18 | import java.io.Closeable;
19 | import java.util.concurrent.Callable;
20 | import java.util.concurrent.ScheduledFuture;
21 | import java.util.concurrent.TimeUnit;
22 |
23 | public interface ServicesContext extends Closeable {
24 | ScheduledFuture> schedule(Runnable runnable, long initialDelay, long delay, TimeUnit units);
25 |
26 | void call(Callable> callable);
27 | }
28 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/com/sshtools/forker/services/ServicesListener.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.services;
17 |
18 | public interface ServicesListener {
19 |
20 | default void stateChanged(Service service) {
21 | }
22 |
23 | default void extendedStatusChanged(Service service, ExtendedServiceStatus extStatus) {
24 | }
25 |
26 | default void serviceAdded(Service service) {
27 | }
28 |
29 | default void serviceRemoved(Service service) {
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/com/sshtools/forker/services/impl/AbstractServiceService.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.services.impl;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | import com.sshtools.forker.services.ExtendedServiceStatus;
22 | import com.sshtools.forker.services.Service;
23 | import com.sshtools.forker.services.ServiceService;
24 | import com.sshtools.forker.services.ServicesListener;
25 |
26 | public abstract class AbstractServiceService implements ServiceService {
27 |
28 | private List listeners = new ArrayList<>();
29 |
30 | @Override
31 | public void addListener(ServicesListener listener) {
32 | listeners.add(listener);
33 | }
34 |
35 | @Override
36 | public void removeListener(ServicesListener listener) {
37 | listeners.remove(listener);
38 | }
39 |
40 | @Override
41 | public void pauseService(Service service) throws Exception {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Override
46 | public void unpauseService(Service service) throws Exception {
47 | throw new UnsupportedOperationException();
48 | }
49 |
50 | protected void fireServiceRemoved(Service s) {
51 | // Removed
52 | for (ServicesListener l : listeners) {
53 | l.serviceRemoved(s);
54 | }
55 | }
56 |
57 | protected void fireStateChange(Service s) {
58 | // PointerState has change
59 | for (ServicesListener l : listeners) {
60 | l.stateChanged(s);
61 | }
62 | }
63 |
64 | protected void fireServiceAdded(Service s) {
65 | // This is a new service
66 | for (ServicesListener l : listeners) {
67 | l.serviceAdded(s);
68 | }
69 | }
70 |
71 | protected void fireExtendedServiceStatusChanged(Service service, ExtendedServiceStatus newStatus) {
72 | for (ServicesListener l : listeners) {
73 | l.extendedStatusChanged(service, newStatus);
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/forker-services/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * PTY module
3 | */
4 | module com.sshtools.forker.services {
5 | requires transitive com.sshtools.forker.common;
6 | requires transitive com.sshtools.forker.client;
7 | requires java.logging;
8 | requires java.desktop;
9 | requires org.freedesktop.dbus;
10 | requires de.thjom.java.systemd;
11 | exports com.sshtools.forker.services;
12 | exports com.sshtools.forker.services.impl;
13 |
14 | }
--------------------------------------------------------------------------------
/forker-updater-console/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | *.classpath
3 | *.project
4 | /.settings/
5 |
--------------------------------------------------------------------------------
/forker-updater-console/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | forker-updater-console
6 | Forker Update Console
7 | Console implementation of updater / installer.
8 |
9 | com.sshtools
10 | forker
11 | 1.8
12 | ..
13 |
14 |
15 |
16 | src/main/java
17 | src/test/java
18 | target/classes
19 | target/test-classes
20 |
21 |
22 | .
23 | src/main/resources
24 |
25 |
26 |
27 |
28 | .
29 | src/test/resources
30 |
31 |
32 |
33 |
34 |
35 |
36 | ${project.groupId}
37 | ${project.version}
38 | forker-updater
39 |
40 |
41 | org.fusesource.jansi
42 | jansi
43 | 2.2.0
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/forker-updater-console/src/main/java/com/sshtools/forker/updater/console/ConsoleSystem.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater.console;
2 |
3 | import org.fusesource.jansi.AnsiConsole;
4 |
5 | public class ConsoleSystem {
6 |
7 | private static ConsoleSystem instance;
8 |
9 | public static ConsoleSystem get() {
10 | if (instance == null) {
11 | instance = new ConsoleSystem();
12 | }
13 | return instance;
14 | }
15 |
16 | private boolean enabled;
17 |
18 | private ConsoleSystem() {
19 | }
20 |
21 | public void activate() {
22 | if (!enabled) {
23 | AnsiConsole.systemInstall();
24 | enabled = true;
25 | }
26 | }
27 |
28 | public void deactivate() {
29 | if (enabled) {
30 | AnsiConsole.systemUninstall();
31 | enabled = false;
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/forker-updater-console/src/main/java/com/sshtools/forker/updater/console/ConsoleUpdateHandler.java:
--------------------------------------------------------------------------------
1 |
2 | package com.sshtools.forker.updater.console;
3 |
4 | import java.util.concurrent.Callable;
5 |
6 | import org.fusesource.jansi.Ansi;
7 | import org.fusesource.jansi.Ansi.Erase;
8 |
9 | import com.sshtools.forker.updater.Entry;
10 | import com.sshtools.forker.updater.UpdateHandler;
11 | import com.sshtools.forker.updater.UpdateSession;
12 |
13 | public class ConsoleUpdateHandler extends AbstractConsoleHandler implements UpdateHandler {
14 |
15 | @Override
16 | public void startDownloads() throws Exception {
17 | println("Updating");
18 | }
19 |
20 | @Override
21 | public void startDownloadFile(Entry file, int index) throws Exception {
22 | this.currentIndex = index;
23 | this.currentDest = file.path().getFileName().toString();
24 | this.currentFrac = 0;
25 | updateRow();
26 | }
27 |
28 | @Override
29 | public void updateDownloadFileProgress(Entry file, float frac) throws Exception {
30 | }
31 |
32 | @Override
33 | public void doneDownloadFile(Entry file) throws Exception {
34 | }
35 |
36 | @Override
37 | public void updateDone(boolean updateError) {
38 | Ansi ansi = Ansi.ansi();
39 | println(ansi.cursorToColumn(0).bold().a("Update complete.").eraseLine(Erase.FORWARD).toString());
40 | }
41 |
42 | @Override
43 | public void updateDownloadProgress(float frac) throws Exception {
44 | currentFrac = frac;
45 | updateRow();
46 | }
47 |
48 | @Override
49 | public void startUpdateRollback() {
50 | Ansi ansi = Ansi.ansi();
51 | println(ansi.cursorToColumn(0).bold().a("Starting rollback.").eraseLine(Erase.FORWARD).toString());
52 | this.currentIndex = 0;
53 | this.currentDest = "Rollback";
54 | this.currentFrac = 0;
55 | updateRow();
56 | }
57 |
58 | @Override
59 | public void updateRollbackProgress(float progress) {
60 | currentFrac = progress;
61 | updateRow();
62 | }
63 |
64 | @Override
65 | public Void prep(Callable callback) {
66 | return null;
67 | }
68 |
69 | @Override
70 | public Void value() {
71 | return null;
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/forker-updater-console/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module com.sshtools.forker.updater.console {
2 | requires transitive com.sshtools.forker.updater;
3 | requires transitive org.fusesource.jansi;
4 | exports com.sshtools.forker.updater.console;
5 | provides com.sshtools.forker.updater.UpdateHandler with com.sshtools.forker.updater.console.ConsoleUpdateHandler;
6 | provides com.sshtools.forker.updater.InstallHandler with com.sshtools.forker.updater.console.ConsoleInstallHandler;
7 | }
--------------------------------------------------------------------------------
/forker-updater-console/src/main/resources/META-INF/services/com.sshtools.forker.updater.InstallHandler:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.updater.console.ConsoleInstallHandler
--------------------------------------------------------------------------------
/forker-updater-console/src/main/resources/META-INF/services/com.sshtools.forker.updater.UpdateHandler:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.updater.console.ConsoleUpdateHandler
--------------------------------------------------------------------------------
/forker-updater-example/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | *.classpath
3 | *.project
4 | /.settings/
5 |
--------------------------------------------------------------------------------
/forker-updater-example/src/main/installer/left-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sshtools/forker/cd8439c8ff5a9f4a1798accadd4e9b75063a444e/forker-updater-example/src/main/installer/left-banner.png
--------------------------------------------------------------------------------
/forker-updater-example/src/main/java/hello/world/HelloWorld.java:
--------------------------------------------------------------------------------
1 | package hello.world;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 | import java.awt.event.WindowAdapter;
6 | import java.awt.event.WindowEvent;
7 |
8 | import javax.swing.JFrame;
9 | import javax.swing.JLabel;
10 |
11 | @SuppressWarnings("serial")
12 | public class HelloWorld extends JFrame {
13 |
14 | public HelloWorld() {
15 | super("HelloWorld");
16 | setPreferredSize(new Dimension(300, 200));
17 | setLayout(new BorderLayout());
18 | add(new JLabel("Hello World!", JLabel.CENTER), BorderLayout.CENTER);
19 | }
20 |
21 | public static void main(String[] args) {
22 | HelloWorld world = new HelloWorld();
23 | world.addWindowListener(new WindowAdapter() {
24 | @Override
25 | public void windowClosing(WindowEvent e) {
26 | System.exit(0);
27 | }
28 | });
29 | world.pack();
30 | world.setVisible(true);
31 | world.setLocation(200, 200);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/forker-updater-example/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module hello.world {
2 | requires java.desktop;
3 | exports hello.world;
4 | opens hello.world;
5 | }
--------------------------------------------------------------------------------
/forker-updater-maven-plugin/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | /.classpath
4 | /.project
5 |
--------------------------------------------------------------------------------
/forker-updater-maven-plugin/src/main/java/com/sshtools/forker/updater/maven/plugin/App.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater.maven.plugin;
2 |
3 | public class App {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/forker-updater-maven-plugin/src/main/java/com/sshtools/forker/updater/maven/plugin/AppFile.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater.maven.plugin;
2 |
3 | import org.apache.maven.plugins.annotations.Parameter;
4 |
5 | public final class AppFile {
6 |
7 | @Parameter(property = "source", required = true) String source;
8 |
9 | @Parameter(property = "target", defaultValue = ".", required = true) String target = ".";
10 |
11 | }
--------------------------------------------------------------------------------
/forker-updater-maven-plugin/src/main/java/com/sshtools/forker/updater/maven/plugin/BootstrapFile.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater.maven.plugin;
2 |
3 | import org.apache.maven.plugins.annotations.Parameter;
4 |
5 | public final class BootstrapFile {
6 |
7 | @Parameter(property = "source", required = true) String source;
8 |
9 | @Parameter(property = "target", defaultValue = ".", required = true) String target = ".";
10 |
11 | }
--------------------------------------------------------------------------------
/forker-updater-maven-plugin/src/main/java/com/sshtools/forker/updater/maven/plugin/SelfExtractingExecutableBuilder.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater.maven.plugin;
2 |
3 | import java.io.IOException;
4 | import java.io.PrintWriter;
5 | import java.nio.file.DirectoryStream;
6 | import java.nio.file.Files;
7 | import java.nio.file.Path;
8 |
9 | import org.apache.maven.plugin.logging.Log;
10 |
11 | import com.sshtools.forker.client.OSCommand;
12 | import com.sun.jna.Platform;
13 |
14 | public class SelfExtractingExecutableBuilder {
15 |
16 | private Path output;
17 | private Path script;
18 | private Path image;
19 | private Log log;
20 |
21 | public Path output() {
22 | return output;
23 | }
24 |
25 | public SelfExtractingExecutableBuilder output(Path output) {
26 | this.output = output;
27 | return this;
28 | }
29 |
30 | public Path script() {
31 | return script;
32 | }
33 |
34 | public SelfExtractingExecutableBuilder script(Path script) {
35 | this.script = script;
36 | return this;
37 | }
38 |
39 | public Path image() {
40 | return image;
41 | }
42 |
43 | public SelfExtractingExecutableBuilder image(Path image) {
44 | this.image = image;
45 | return this;
46 | }
47 |
48 | public void make() throws IOException {
49 | if(!Files.exists(output))
50 | Files.createDirectories(output);
51 |
52 | if (Platform.isWindows() ? OSCommand.hasCommand("makensisw") : OSCommand.hasCommand("makensis")) {
53 | Path artifactName = output.getFileName();
54 | Path nsisDir = image;
55 | Path nsisPath = nsisDir.resolve(artifactName.toString() + ".nsis");
56 | try {
57 | try (PrintWriter w = new PrintWriter(
58 | Files.newBufferedWriter(nsisPath))) {
59 | w.println("#");
60 | w.println(String.format("OutFile \"%s\"",
61 | output.resolve(artifactName.toString() + ".exe")));
62 | w.println();
63 | w.println("# set desktop as install directory");
64 | w.println("InstallDir $TEMP");
65 | w.println();
66 | w.println("# default section start; every NSIS script has at least one section.");
67 | w.println("Section");
68 | w.println();
69 | w.println(" SetOutPath $INSTDIR");
70 |
71 | try (DirectoryStream stream = Files.newDirectoryStream(image)) {
72 | for (Path path : stream) {
73 | if(path.equals(nsisPath)) {
74 | if (Files.isDirectory(path)) {
75 | w.println(String.format(" File /r \"%s\"", path));
76 | }
77 | else {
78 | w.println(String.format(" File \"%s\"", path));
79 | }
80 | }
81 | }
82 | }
83 |
84 | w.println();
85 | w.println("# default section end.");
86 | w.println("SectionEnd");
87 | }
88 |
89 | OSCommand.runCommand(Platform.isWindows() ? "makensisw.exe" : "makensis",
90 | nsisPath.toString());
91 |
92 |
93 | }
94 | finally {
95 | /* Clean up */
96 | Files.delete(nsisPath);
97 | }
98 |
99 | return;
100 | }
101 | throw new UnsupportedOperationException();
102 | }
103 |
104 | public Log log() {
105 | return log;
106 | }
107 |
108 | public SelfExtractingExecutableBuilder log(Log log) {
109 | this.log = log;
110 | return this;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/forker-updater-swt/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | *.classpath
3 | *.project
4 | /.settings/
5 |
--------------------------------------------------------------------------------
/forker-updater-swt/src/main/.gitignore:
--------------------------------------------------------------------------------
1 | /java11-processed/
2 |
--------------------------------------------------------------------------------
/forker-updater-swt/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module com.sshtools.forker.updater.swt {
2 | requires java.desktop;
3 | requires java.prefs;
4 | requires transitive org.eclipse.swt;
5 |
6 | /* Jenkins is linux so we must use this for now until a better solution
7 | * is found. SWT (OSGi?) does not play well with JPMS
8 | */
9 | requires transitive org.eclipse.swt.gtk.linux.x86_64;
10 | // requires static org.eclipse.swt.win32.win32.x86_64;
11 |
12 | requires transitive com.sshtools.forker.updater;
13 | requires transitive com.sshtools.forker.wrapper;
14 | exports com.sshtools.forker.updater.swt;
15 | opens com.sshtools.forker.updater.swt;
16 | provides com.sshtools.forker.updater.UpdateHandler with com.sshtools.forker.updater.swt.SWTUpdateHandler;
17 | provides com.sshtools.forker.updater.InstallHandler with com.sshtools.forker.updater.swt.SWTInstallHandler;
18 | provides com.sshtools.forker.updater.UninstallHandler with com.sshtools.forker.updater.swt.SWTUninstallHandler;
19 | }
--------------------------------------------------------------------------------
/forker-updater-swt/src/main/resources/META-INF/services/com.sshtools.forker.updater.InstallHandler:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.updater.swt.SWTInstallHandler
--------------------------------------------------------------------------------
/forker-updater-swt/src/main/resources/META-INF/services/com.sshtools.forker.updater.UninstallHandler:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.updater.swt.SWTUninstallHandler
--------------------------------------------------------------------------------
/forker-updater-swt/src/main/resources/META-INF/services/com.sshtools.forker.updater.UpdateHandler:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.updater.swt.SWTUpdateHandler
--------------------------------------------------------------------------------
/forker-updater-swt/src/main/resources/com/sshtools/forker/updater/swt/Bootstrap.properties:
--------------------------------------------------------------------------------
1 | title=Snake Updater
--------------------------------------------------------------------------------
/forker-updater-swt/src/main/resources/com/sshtools/forker/updater/swt/Install.properties:
--------------------------------------------------------------------------------
1 | min=\uf2d1
2 | close=\uf2d3
3 | preparing=Preparing
4 | title=Snake Installer
5 | browse=Browse
6 | target=Directory:
7 | install=\uf019
8 | failed=Failed. {0}
9 | success=Success, launching!
10 | startInstall=Starting installation.
11 | installFile=Installing file {0}.
12 | installFileDone=Installed file {0}.
13 | selectTarget=Choose Destination Folder
14 |
--------------------------------------------------------------------------------
/forker-updater-swt/src/main/resources/com/sshtools/forker/updater/swt/Update.properties:
--------------------------------------------------------------------------------
1 | min=\uf2d1
2 | close=\uf2d3
3 | title=Snake
4 | check=Checking for updates ..
5 | ready=Ready to update!
6 | startCheckUpdates=Update check started.
7 | startCheckUpdateFile=Checking {0}.
8 | doneCheckUpdateFile=Checked {0}.
9 | doneCheckUpdates=Update check done.
10 | startDownloads=Starting downloads.
11 | startDownloadFile=Downloading file {0}.
12 | validatingFile=Validating {0}.
13 | doneDownloadFile=Done downloading {0}.
14 | doneDownloads=Downloads complete.
15 | failed=Failed. {0}
16 | success=Success, launching!
17 | noUpdates=No updates, launching now!
--------------------------------------------------------------------------------
/forker-updater/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | *.classpath
4 | /bin/
5 | *.project
6 |
--------------------------------------------------------------------------------
/forker-updater/README.md:
--------------------------------------------------------------------------------
1 | # Forker Updater
2 |
3 | This is the core module for Forker Updater System. This extends Forker Wrapper to allow self extracting installers or archives for updateable applications with minimal configuration for Maven projects.
4 |
5 | ## Goals
6 |
7 | * Support 3 major operating systems initially. WIP - Linux complete, Windows partial. Others TBG
8 | * As close to zero configuration as possible, either discovering or using convention over configuration. WIP - More analysis to discover system modules, quirks etc.
9 | * Non-destructive, installation and updates can rollback at any failure point.
10 | * Efficient. Only download files that need updating.
11 | * Consistent. A freshly installed application has exactly the same layout as files in the update repository.
12 | * Familiar. Apps can be distributed as simple archives, or self extracting executables. WIP more operating system support and formats required.
13 | * No special update server required, it can be just an HTTP server.
14 | * Multiple toolkit support. Depending on needs, simple console, colour console or SWT UIs can be used. Further toolkits can be easily added (Swing planned, JavaFX may be ported back from Snake project).
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/forker-updater/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | forker-updater
4 | Forker Updater
5 | Extends forker-wrapper to add update features.
6 |
7 | com.sshtools
8 | forker
9 | 1.8
10 | ..
11 |
12 |
13 |
14 | src/main/java
15 | src/test/java
16 | target/classes
17 | target/test-classes
18 |
19 |
20 | .
21 | src/main/resources
22 |
23 |
24 |
25 |
26 | .
27 | src/test/resources
28 |
29 |
30 |
31 |
32 | com.github.maven-nar
33 | nar-maven-plugin
34 | 3.2.3
35 | true
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ${project.groupId}
44 | forker-wrapper
45 | ${project.version}
46 |
47 |
48 |
49 |
50 |
51 | ext-snapshots-local
52 | ext-snapshots-local
53 | https://artifactory.jadaptive.com/ext-snapshots-local
54 |
55 | false
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/AbstractHandler.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.io.Console;
4 | import java.io.PrintWriter;
5 |
6 | public abstract class AbstractHandler implements Handler {
7 |
8 | protected Console console;
9 | protected PrintWriter out;
10 |
11 | protected AbstractHandler() {
12 | console = System.console();
13 | out = console == null ? new PrintWriter(System.out) : console.writer();
14 | }
15 |
16 | protected void println(String str) {
17 | out.println(str);
18 | }
19 |
20 | protected void print(String str) {
21 | out.print(str);
22 | }
23 |
24 | @Override
25 | public boolean isCancelled() {
26 | return false;
27 | }
28 |
29 | @Override
30 | public void complete() {
31 | }
32 |
33 | @Override
34 | public void failed(Throwable error) {
35 | error.printStackTrace(out);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/DefaultConsoleUninstallHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mordechai Meisels
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy of
6 | * the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations under
14 | * the License.
15 | */
16 | package com.sshtools.forker.updater;
17 |
18 | import java.io.BufferedReader;
19 | import java.io.IOException;
20 | import java.io.InputStreamReader;
21 | import java.nio.file.Path;
22 | import java.util.concurrent.Callable;
23 |
24 | public class DefaultConsoleUninstallHandler extends AbstractHandler implements UninstallHandler {
25 |
26 | private boolean deleteAll;
27 |
28 | @Override
29 | public void init(UninstallSession context) {
30 | }
31 |
32 | @Override
33 | public void startUninstall() throws Exception {
34 | }
35 |
36 | @Override
37 | public void uninstallFile(Path file, Path dest, int index) throws Exception {
38 | }
39 |
40 | @Override
41 | public void uninstallFileProgress(Path file, float frac) throws Exception {
42 | }
43 |
44 | @Override
45 | public void uninstallProgress(float frac) throws Exception {
46 | }
47 |
48 | @Override
49 | public void uninstallFileDone(Path file) throws Exception {
50 | }
51 |
52 | @Override
53 | public void uninstallDone() {
54 | }
55 |
56 | @Override
57 | public Boolean prep(Callable callable) {
58 | String answer = prompt("Delete all files (Y)es/(No)?: ");
59 | return deleteAll = answer.equals("y") || answer.equals("yes");
60 | }
61 |
62 | private String prompt(String text, Object... args) {
63 | if (console == null) {
64 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
65 | print(String.format(text, args));
66 | return reader.readLine();
67 | } catch (IOException ioe) {
68 | throw new IllegalStateException("Failed to get prompt.", ioe);
69 | }
70 | } else {
71 | return console.readLine(text, args);
72 | }
73 | }
74 |
75 | @Override
76 | public Boolean value() {
77 | return deleteAll;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/DefaultConsoleUpdateHandler.java:
--------------------------------------------------------------------------------
1 |
2 | package com.sshtools.forker.updater;
3 |
4 | import java.util.concurrent.Callable;
5 |
6 | public class DefaultConsoleUpdateHandler extends AbstractHandler implements UpdateHandler {
7 |
8 | @Override
9 | public void init(UpdateSession context) {
10 | }
11 |
12 | @Override
13 | public void startDownloads() throws Exception {
14 | }
15 |
16 | @Override
17 | public void startDownloadFile(Entry file, int index) throws Exception {
18 | }
19 |
20 | @Override
21 | public void updateDownloadFileProgress(Entry file, float frac) throws Exception {
22 | }
23 |
24 | @Override
25 | public void doneDownloadFile(Entry file) throws Exception {
26 | }
27 |
28 | @Override
29 | public void updateDone(boolean upgradeError) {
30 | }
31 |
32 | @Override
33 | public void updateDownloadProgress(float frac) throws Exception {
34 | }
35 |
36 | @Override
37 | public void startUpdateRollback() {
38 | System.out.println("Rolling back.");
39 | }
40 |
41 | @Override
42 | public void updateRollbackProgress(float progress) {
43 | if(progress == 1)
44 | System.out.println("Rollback complete.");
45 |
46 | }
47 |
48 | @Override
49 | public Void prep(Callable callback) {
50 | return null;
51 | }
52 |
53 | @Override
54 | public Void value() {
55 | return null;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/Handler.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.util.concurrent.Callable;
4 |
5 | public interface Handler {
6 |
7 | boolean isCancelled();
8 |
9 | void init(S session);
10 |
11 | void complete();
12 |
13 | void failed(Throwable error);
14 |
15 | V prep(Callable callback);
16 |
17 | V value();
18 | }
19 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/InstallHandler.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.nio.file.Path;
4 |
5 | public interface InstallHandler extends Handler {
6 |
7 | void startInstall() throws Exception;
8 |
9 | void installProgress(float frac) throws Exception;
10 |
11 | void installFile(Path file, Path d, int index) throws Exception;
12 |
13 | void installFileProgress(Path file, float progress) throws Exception;
14 |
15 | void installFileDone(Path file) throws Exception;
16 |
17 | void startInstallRollback() throws Exception;
18 |
19 | void installRollbackProgress(float progress);
20 |
21 | void installDone();
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/InstallSession.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Path;
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | public class InstallSession extends AbstractSession {
11 |
12 | private List files = new ArrayList<>();
13 | private Path base;
14 | private long sz = -1;
15 |
16 | public InstallSession() {
17 | super();
18 | }
19 |
20 | public InstallSession(Path propertiesFile) throws IOException {
21 | super(propertiesFile);
22 | }
23 |
24 | public Path base() {
25 | return base;
26 | }
27 |
28 | @Override
29 | public int updates() {
30 | return files.size();
31 | }
32 |
33 | public List files() {
34 | return Collections.unmodifiableList(files);
35 | }
36 |
37 | public InstallSession addFile(Path path) {
38 | files.add(path);
39 | if (sz > -1) {
40 | sz = -1;
41 | }
42 | return this;
43 | }
44 |
45 | public InstallSession base(Path base) {
46 | this.base = base;
47 | return this;
48 | }
49 |
50 | public long size() {
51 | if (sz == -1) {
52 | sz = 0;
53 | try {
54 | for (Path p : files()) {
55 | sz += Files.size(p);
56 | }
57 | } catch (IOException ioe) {
58 | throw new IllegalStateException("Could not get update size.", ioe);
59 | }
60 | }
61 | return sz;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/InstallerResults.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | public interface InstallerResults {
4 |
5 | void upgradeAndLaunch() throws Exception;
6 |
7 | void upgradeAndExit(int exit);
8 |
9 | void exit(int exit);
10 |
11 | void upgradeAndWait();
12 | }
13 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/Launcher.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class Launcher {
7 |
8 | private List entries = new ArrayList<>();
9 | private String id;
10 |
11 | public Launcher(String id) {
12 | this.id = id;
13 | }
14 |
15 | public String id() {
16 | return id;
17 | }
18 |
19 | public List entries() {
20 | return entries;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/NotFatalException.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | @SuppressWarnings("serial")
4 | public class NotFatalException extends Exception {
5 |
6 | public NotFatalException(String message, Throwable cause) {
7 | super(message, cause);
8 | }
9 |
10 | public NotFatalException(String message) {
11 | super(message);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/Session.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.util.Properties;
4 |
5 | public interface Session {
6 |
7 | int updates();
8 |
9 | long size();
10 |
11 | Properties properties();
12 | }
13 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/ThrottledInputStream.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 |
6 | public class ThrottledInputStream extends InputStream {
7 |
8 | private final InputStream inputStream;
9 | private final long maxBytesPerSec;
10 | private final long startTime = System.nanoTime();
11 |
12 | private long bytesRead = 0;
13 | private long totalSleepTime = 0;
14 |
15 | private static final long SLEEP_DURATION_MS = 30;
16 |
17 | public ThrottledInputStream(InputStream inputStream) {
18 | this(inputStream, Long.MAX_VALUE);
19 | }
20 |
21 | public ThrottledInputStream(InputStream inputStream, long maxBytesPerSec) {
22 | if (maxBytesPerSec < 0) {
23 | throw new IllegalArgumentException("maxBytesPerSec shouldn't be negative");
24 | }
25 | if (inputStream == null) {
26 | throw new IllegalArgumentException("inputStream shouldn't be null");
27 | }
28 |
29 | this.inputStream = inputStream;
30 | this.maxBytesPerSec = maxBytesPerSec;
31 | }
32 |
33 | @Override
34 | public void close() throws IOException {
35 | inputStream.close();
36 | }
37 |
38 | @Override
39 | public int read() throws IOException {
40 | throttle();
41 | int data = inputStream.read();
42 | if (data != -1) {
43 | bytesRead++;
44 | }
45 | return data;
46 | }
47 |
48 | @Override
49 | public int read(byte[] b) throws IOException {
50 | throttle();
51 | int readLen = inputStream.read(b);
52 | if (readLen != -1) {
53 | bytesRead += readLen;
54 | }
55 | return readLen;
56 | }
57 |
58 | @Override
59 | public int read(byte[] b, int off, int len) throws IOException {
60 | throttle();
61 | int readLen = inputStream.read(b, off, len);
62 | if (readLen != -1) {
63 | bytesRead += readLen;
64 | }
65 | return readLen;
66 | }
67 |
68 | private void throttle() throws IOException {
69 | while (getBytesPerSec() > maxBytesPerSec) {
70 | try {
71 | Thread.sleep(SLEEP_DURATION_MS);
72 | totalSleepTime += SLEEP_DURATION_MS;
73 | } catch (InterruptedException e) {
74 | System.out.println("Thread interrupted" + e.getMessage());
75 | throw new IOException("Thread interrupted", e);
76 | }
77 | }
78 | }
79 |
80 | public long getTotalBytesRead() {
81 | return bytesRead;
82 | }
83 |
84 | /**
85 | * Return the number of bytes read per second
86 | */
87 | public long getBytesPerSec() {
88 | long elapsed = (System.nanoTime() - startTime) / 1000000000;
89 | if (elapsed == 0) {
90 | return bytesRead;
91 | } else {
92 | return bytesRead / elapsed;
93 | }
94 | }
95 |
96 | public long getTotalSleepTime() {
97 | return totalSleepTime;
98 | }
99 |
100 | @Override
101 | public String toString() {
102 | return "ThrottledInputStream{" + "bytesRead=" + bytesRead + ", maxBytesPerSec=" + maxBytesPerSec
103 | + ", bytesPerSec=" + getBytesPerSec() + ", totalSleepTimeInSeconds=" + totalSleepTime / 1000 + '}';
104 | }
105 | }
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/ThrottledOutputStream.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 |
6 | public class ThrottledOutputStream extends OutputStream {
7 | private OutputStream outputStream;
8 | private final long maxBytesPerSecond;
9 | private final long startTime = System.nanoTime();
10 |
11 | private long bytesWrite = 0;
12 | private long totalSleepTime = 0;
13 | private static final long SLEEP_DURATION_MS = 30;
14 |
15 | public ThrottledOutputStream(OutputStream outputStream) {
16 | this(outputStream, Long.MAX_VALUE);
17 | }
18 |
19 | public ThrottledOutputStream(OutputStream outputStream, long maxBytesPerSecond) {
20 | if (outputStream == null) {
21 | throw new IllegalArgumentException("outputStream shouldn't be null");
22 | }
23 |
24 | if (maxBytesPerSecond <= 0) {
25 | throw new IllegalArgumentException("maxBytesPerSecond should be greater than zero");
26 | }
27 |
28 | this.outputStream = outputStream;
29 | this.maxBytesPerSecond = maxBytesPerSecond;
30 | }
31 |
32 | @Override
33 | public void write(int arg0) throws IOException {
34 | throttle();
35 | outputStream.write(arg0);
36 | bytesWrite++;
37 | }
38 |
39 | @Override
40 | public void write(byte[] b, int off, int len) throws IOException {
41 | if (len < maxBytesPerSecond) {
42 | throttle();
43 | bytesWrite = bytesWrite + len;
44 | outputStream.write(b, off, len);
45 | return;
46 | }
47 |
48 | long currentOffSet = off;
49 | long remainingBytesToWrite = len;
50 |
51 | do {
52 | throttle();
53 | remainingBytesToWrite = remainingBytesToWrite - maxBytesPerSecond;
54 | bytesWrite = bytesWrite + maxBytesPerSecond;
55 | outputStream.write(b, (int) currentOffSet, (int) maxBytesPerSecond);
56 | currentOffSet = currentOffSet + maxBytesPerSecond;
57 |
58 | } while (remainingBytesToWrite > maxBytesPerSecond);
59 |
60 | throttle();
61 | bytesWrite = bytesWrite + remainingBytesToWrite;
62 | outputStream.write(b, (int) currentOffSet, (int) remainingBytesToWrite);
63 | }
64 |
65 | @Override
66 | public void write(byte[] b) throws IOException {
67 | this.write(b, 0, b.length);
68 | }
69 |
70 | public void throttle() throws IOException {
71 | while (getBytesPerSec() > maxBytesPerSecond) {
72 | try {
73 | Thread.sleep(SLEEP_DURATION_MS);
74 | totalSleepTime += SLEEP_DURATION_MS;
75 | } catch (InterruptedException e) {
76 | System.out.println("Thread interrupted" + e.getMessage());
77 | throw new IOException("Thread interrupted", e);
78 | }
79 | }
80 | }
81 |
82 | /**
83 | * Return the number of bytes read per second
84 | */
85 | public long getBytesPerSec() {
86 | long elapsed = (System.nanoTime() - startTime) / 1000000000;
87 | if (elapsed == 0) {
88 | return bytesWrite;
89 | } else {
90 | return bytesWrite / elapsed;
91 | }
92 | }
93 |
94 | @Override
95 | public String toString() {
96 | return "ThrottledOutputStream{" + "bytesWrite=" + bytesWrite + ", maxBytesPerSecond=" + maxBytesPerSecond
97 | + ", bytesPerSec=" + getBytesPerSec() + ", totalSleepTimeInSeconds=" + totalSleepTime / 1000 + '}';
98 | }
99 |
100 | public void close() throws IOException {
101 | outputStream.close();
102 | }
103 | }
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/UndoableOp.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.io.IOException;
4 |
5 | public interface UndoableOp {
6 | void undo() throws IOException;
7 |
8 | default void cleanUp() {
9 | }
10 | }
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/UninstallHandler.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.nio.file.Path;
4 |
5 | public interface UninstallHandler extends Handler {
6 |
7 | void startUninstall() throws Exception;
8 |
9 | void uninstallProgress(float frac) throws Exception;
10 |
11 | void uninstallFile(Path file, Path d, int index) throws Exception;
12 |
13 | void uninstallFileProgress(Path file, float progress) throws Exception;
14 |
15 | void uninstallFileDone(Path file) throws Exception;
16 |
17 | void uninstallDone();
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/UninstallSession.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Path;
6 | import java.util.Collections;
7 | import java.util.LinkedHashSet;
8 | import java.util.Set;
9 |
10 | public class UninstallSession extends AbstractSession {
11 |
12 | private Set files = new LinkedHashSet<>();
13 | private Path base;
14 | private long sz = -1;
15 |
16 | public UninstallSession() throws IOException {
17 | super();
18 | }
19 |
20 | public UninstallSession(Path propertiesFile) throws IOException {
21 | super(propertiesFile);
22 | }
23 |
24 | public Path base() {
25 | return base;
26 | }
27 |
28 | @Override
29 | public int updates() {
30 | return files.size();
31 | }
32 |
33 | public Set files() {
34 | return Collections.unmodifiableSet(files);
35 | }
36 |
37 | public UninstallSession addDirectory(Path path) {
38 | if(Files.exists(path)) {
39 | try {
40 | Files.walk(path).forEach(s -> {
41 | if (Files.isDirectory(s))
42 | addFile(s);
43 | });
44 | }
45 | catch(IOException ioe) {
46 | throw new IllegalStateException(String.format("Failed to add directory %s to uninstall list."));
47 | }
48 | }
49 | return this;
50 | }
51 |
52 | public UninstallSession addFile(Path path) {
53 | files.add(path);
54 | if (sz > -1) {
55 | sz = -1;
56 | }
57 | return this;
58 | }
59 |
60 | public UninstallSession base(Path base) {
61 | this.base = base;
62 | return this;
63 | }
64 |
65 | @Override
66 | public long size() {
67 | return updates();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/UpdateHandler.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater;
2 |
3 | import java.net.URL;
4 | import java.util.concurrent.Callable;
5 |
6 | public interface UpdateHandler extends Handler {
7 |
8 | default void startingManifestLoad(URL location) {
9 | }
10 |
11 | default void completedManifestLoad(URL location) {
12 | }
13 |
14 | void doneDownloadFile(Entry file) throws Exception;
15 |
16 | void updateDownloadFileProgress(Entry file, float progress) throws Exception;
17 |
18 | void updateDownloadProgress(float progress) throws Exception;
19 |
20 | void startDownloadFile(Entry file, int index) throws Exception;
21 |
22 | void startDownloads() throws Exception;
23 |
24 | default boolean noUpdates(Callable task) {
25 | return true;
26 | }
27 |
28 | default boolean updatesComplete(Callable task) throws Exception {
29 | return true;
30 | }
31 |
32 | void updateDone(boolean upgradeError);
33 |
34 | void startUpdateRollback();
35 |
36 | void updateRollbackProgress(float progress);
37 | }
38 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/test/InstallTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater.test;
2 |
3 | import java.io.IOException;
4 | import java.io.InterruptedIOException;
5 | import java.nio.file.Path;
6 | import java.nio.file.Paths;
7 | import java.util.concurrent.Callable;
8 |
9 | import com.sshtools.forker.updater.InstallHandler;
10 | import com.sshtools.forker.updater.InstallSession;
11 |
12 |
13 | public class InstallTest {
14 | public static void main(String[] args, InstallHandler installHandler) throws Exception {
15 | InstallSession s = new InstallSession();
16 | s.properties().putAll(System.getProperties());
17 | s.base(Paths.get(System.getProperty("user.dir")));
18 | installHandler.init(s);
19 | Path destination = installHandler.prep(new Callable() {
20 | @Override
21 | public Void call() throws Exception {
22 | installTo(installHandler, s, installHandler.value());
23 | return null;
24 | }
25 |
26 | });
27 | if (destination == null)
28 | return;
29 | installTo(installHandler, s, destination);
30 | }
31 |
32 | protected static void installTo(InstallHandler handler, InstallSession session, Path dest) throws Exception {
33 | handler.startInstall();
34 | float iprg = 0;
35 | try {
36 | Path d = Paths.get(System.getProperty("java.io.tmpdir"));
37 | for (int i = 0; i < 100; i++) {
38 | Path f = Paths.get(String.format("file%d", i));
39 | checkCancel(handler);
40 | handler.installFile(f, d, i);
41 | for (float prg = 0; prg < 1; prg += Math.random()) {
42 | checkCancel(handler);
43 | handler.installFileProgress(f, prg);
44 | handler.installProgress(iprg = ((float) i / 100) + (0.01f * prg));
45 | Thread.sleep(50);
46 | }
47 | checkCancel(handler);
48 | handler.installFileDone(f);
49 | if(i > 50 && Boolean.getBoolean("installTest.testRollback")) {
50 | throw new InterruptedIOException("Failed to install a thing!");
51 | }
52 | }
53 | checkCancel(handler);
54 | handler.installDone();
55 | handler.complete();
56 | } catch (InterruptedIOException iioe) {
57 | handler.startInstallRollback();
58 | for (float prg = iprg; prg >= 0; prg -= 0.01) {
59 | handler.installRollbackProgress(prg);
60 | Thread.sleep(50);
61 | }
62 | handler.failed(iioe);
63 | } catch (Exception e) {
64 | handler.failed(e);
65 | throw e;
66 | }
67 | }
68 |
69 | protected static void checkCancel(InstallHandler handler) throws InterruptedIOException {
70 | if (handler.isCancelled())
71 | throw new InterruptedIOException("Cancelled.");
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/test/UninstallTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater.test;
2 |
3 | import java.io.InterruptedIOException;
4 | import java.nio.file.Path;
5 | import java.nio.file.Paths;
6 | import java.util.concurrent.Callable;
7 |
8 | import com.sshtools.forker.updater.UninstallHandler;
9 | import com.sshtools.forker.updater.UninstallSession;
10 |
11 | public class UninstallTest {
12 | public static void main(String[] args, UninstallHandler uninstallHandler) throws Exception {
13 | UninstallSession s = new UninstallSession();
14 | s.properties().putAll(System.getProperties());
15 | s.base(Paths.get(System.getProperty("user.dir")));
16 | uninstallHandler.init(s);
17 | Boolean deleteAll = uninstallHandler.prep(new Callable() {
18 | @Override
19 | public Void call() throws Exception {
20 | uninstallFrom(uninstallHandler, s, uninstallHandler.value());
21 | return null;
22 | }
23 |
24 | });
25 | if (deleteAll == null)
26 | /*
27 | * No destination means the install handler will prompt soon in its own way,
28 | * call the callback when it has the destination folder
29 | */
30 | return;
31 | uninstallFrom(uninstallHandler, s, deleteAll);
32 |
33 | }
34 |
35 | protected static void uninstallFrom(UninstallHandler handler, UninstallSession session, boolean deleteAll)
36 | throws Exception {
37 | handler.startUninstall();
38 | try {
39 | Path d = Paths.get(System.getProperty("java.io.tmpdir"));
40 | for (int i = 0; i < 100; i++) {
41 | Path f = Paths.get(String.format("file%d", i));
42 | checkCancel(handler);
43 | handler.uninstallFile(f, d, i);
44 | for (float prg = 0; prg < 1; prg += Math.random()) {
45 | checkCancel(handler);
46 | handler.uninstallFileProgress(f, prg);
47 | handler.uninstallProgress(((float) i / 100) + (0.01f * prg));
48 | Thread.sleep(50);
49 | }
50 | checkCancel(handler);
51 | handler.uninstallFileDone(f);
52 | }
53 | checkCancel(handler);
54 | handler.uninstallDone();
55 | handler.complete();
56 | } catch (Exception e) {
57 | handler.failed(e);
58 | throw e;
59 | }
60 | }
61 |
62 | protected static void checkCancel(UninstallHandler handler) throws InterruptedIOException {
63 | if (handler.isCancelled())
64 | throw new InterruptedIOException("Cancelled.");
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtools/forker/updater/test/UpdateTest.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.updater.test;
2 |
3 | import java.io.InterruptedIOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Path;
6 | import java.util.concurrent.Callable;
7 |
8 | import com.sshtools.forker.updater.AppManifest;
9 | import com.sshtools.forker.updater.Entry;
10 | import com.sshtools.forker.updater.InstallHandler;
11 | import com.sshtools.forker.updater.UpdateHandler;
12 | import com.sshtools.forker.updater.UpdateSession;
13 |
14 | public class UpdateTest {
15 | public static void main(String[] args, UpdateHandler updateHandler) throws Exception {
16 | UpdateSession s = new UpdateSession();
17 | s.properties().putAll(System.getProperties());
18 | updateHandler.init(s);
19 | updateHandler.prep(new Callable() {
20 | @Override
21 | public Void call() throws Exception {
22 | update(updateHandler, s);
23 | return null;
24 | }
25 | });
26 | }
27 |
28 | protected static void update(UpdateHandler handler, UpdateSession session) throws Exception {
29 | AppManifest mf = new AppManifest();
30 | handler.startDownloads();
31 | float iprg = 0;
32 | try {
33 | for (int i = 0; i < 100; i++) {
34 | Path pf = Files.createTempFile("abc", "def");
35 | pf.toFile().deleteOnExit();
36 | Entry f = new Entry(pf, mf);
37 | checkCancel(handler);
38 | handler.startDownloadFile(f, i);
39 | for (float prg = 0; prg < 1; prg += Math.random()) {
40 | checkCancel(handler);
41 | handler.updateDownloadFileProgress(f, prg);
42 | handler.updateDownloadProgress(iprg = (((float) i / 100) + (0.01f * prg)));
43 | Thread.sleep(50);
44 | }
45 | checkCancel(handler);
46 | handler.doneDownloadFile(f);
47 | Files.delete(pf);
48 | if (i > 50 && Boolean.getBoolean("updateTest.testRollback")) {
49 | throw new InterruptedIOException("Failed to update a thing!");
50 | }
51 | }
52 | checkCancel(handler);
53 | handler.updateDone(false);
54 | handler.complete();
55 | } catch (InterruptedIOException iioe) {
56 | handler.startUpdateRollback();
57 | for (float prg = iprg; prg >= 0; prg -= 0.01) {
58 | handler.updateRollbackProgress(prg);
59 | Thread.sleep(50);
60 | }
61 | handler.failed(iioe);
62 | } catch (Exception e) {
63 | e.printStackTrace();
64 | handler.failed(e);
65 | throw e;
66 | }
67 | }
68 |
69 | protected static void checkCancel(UpdateHandler handler) throws InterruptedIOException {
70 | if (handler.isCancelled())
71 | throw new InterruptedIOException("Cancelled.");
72 | }
73 |
74 | protected static void checkCancel(InstallHandler handler) throws InterruptedIOException {
75 | if (handler.isCancelled())
76 | throw new InterruptedIOException("Cancelled.");
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtoos/forker/updater/rpc/UpdateServicesMBean.java:
--------------------------------------------------------------------------------
1 | package com.sshtoos.forker.updater.rpc;
2 |
3 | public interface UpdateServicesMBean {
4 |
5 | void open(String[] args);
6 | }
7 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/com/sshtoos/forker/updater/rpc/UpdaterServices.java:
--------------------------------------------------------------------------------
1 | package com.sshtoos.forker.updater.rpc;
2 |
3 | public class UpdaterServices implements UpdateServicesMBean {
4 |
5 | public void open(String[] args) {
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/forker-updater/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Updater module
3 | */
4 | module com.sshtools.forker.updater {
5 | requires transitive java.xml;
6 | requires transitive com.sshtools.forker.wrapper;
7 | exports com.sshtools.forker.updater;
8 | exports com.sshtools.forker.updater.test;
9 | uses com.sshtools.forker.updater.UpdateHandler;
10 | uses com.sshtools.forker.updater.InstallHandler;
11 | uses com.sshtools.forker.updater.UninstallHandler;
12 | opens com.sshtools.forker.updater to info.picocli;
13 | }
--------------------------------------------------------------------------------
/forker-wrapped/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | *.classpath
4 | *.project
5 |
--------------------------------------------------------------------------------
/forker-wrapped/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | forker-wrapped
4 | Forker Wrapped
5 | This module can be added by wrapped applications to access single-instance reuse and other communication with the wrapper. It deliberately has no other dependencies
6 |
7 | com.sshtools
8 | forker
9 | 1.8
10 | ..
11 |
12 |
13 |
14 | src/main/java
15 | src/test/java
16 | target/classes
17 | target/test-classes
18 |
19 |
20 | .
21 | src/main/resources
22 |
23 |
24 |
25 |
26 | .
27 | src/test/resources
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/forker-wrapped/src/main/java/com/sshtools/forker/wrapped/WrappedMXBean.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapped;
2 |
3 | public interface WrappedMXBean {
4 |
5 | int launch(String[] args);
6 |
7 | int shutdown();
8 |
9 | void ping();
10 | }
11 |
--------------------------------------------------------------------------------
/forker-wrapped/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Wrapped module
3 | */
4 | module com.sshtools.forker.wrapped {
5 | exports com.sshtools.forker.wrapped;
6 | requires java.management;
7 | }
--------------------------------------------------------------------------------
/forker-wrapper-plugin-scripts/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | *.classpath
4 | /bin/
5 | *.project
6 |
--------------------------------------------------------------------------------
/forker-wrapper-plugin-scripts/README.md:
--------------------------------------------------------------------------------
1 | # Forker Wrapper Plugin - Scripts
2 |
3 | ## Introduction
4 |
5 | A plugin for Forker Wrapper that allows scripts to be run at significant events. This
6 | used to be part of the core but was separated as part of a modularisation effort.
7 |
--------------------------------------------------------------------------------
/forker-wrapper-plugin-scripts/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | forker-wrapper-plugin-scripts
6 | Forker Wrapper Plugin - Scripts
7 | Extend forker-wrapper allow scripts to be run at significant efents.
8 |
9 | com.sshtools
10 | forker
11 | 1.8
12 | ..
13 |
14 |
15 |
16 | src/main/java
17 | src/test/java
18 | target/classes
19 | target/test-classes
20 |
21 |
22 | .
23 | src/main/resources
24 |
25 |
26 |
27 |
28 | .
29 | src/test/resources
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | com.sshtools
38 | forker-wrapper
39 | ${project.version}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | ext-releases
49 | https://artifactory.jadaptive.com/ext-releases-local/
50 |
51 |
52 | false
53 |
54 |
55 |
56 | ext-snapshots
57 | https://artifactory.jadaptive.com/ext-snapshots-local/
58 |
59 | false
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | public-releases
68 | https://artifactory.jadaptive.com/public-releases
69 |
70 |
71 | false
72 |
73 |
74 |
75 | public-snapshots
76 | https://artifactory.jadaptive.com/public-snapshots
77 |
78 | false
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | opensource-releases
87 | https://artifactory.jadaptive.com/opensource-releases
88 |
89 |
90 | false
91 |
92 |
93 |
94 | opensource-snapshots
95 | https://artifactory.jadaptive.com/opensource-snapshots
96 |
97 | false
98 |
99 |
100 | true
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/forker-wrapper-plugin-scripts/src/main/java/com/sshtools/forker/wrapper/plugin/scripts/ScriptWrapperPlugin.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper.plugin.scripts;
2 |
3 | import java.io.File;
4 | import java.io.FileReader;
5 | import java.io.IOException;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | import javax.script.Bindings;
10 | import javax.script.ScriptEngine;
11 | import javax.script.ScriptEngineManager;
12 | import javax.script.ScriptException;
13 |
14 | import com.sshtools.forker.wrapper.ForkerWrapper;
15 | import com.sshtools.forker.wrapper.KeyValuePair;
16 | import com.sshtools.forker.wrapper.WrapperPlugin;
17 |
18 | public class ScriptWrapperPlugin implements WrapperPlugin {
19 | private ScriptEngine engine;
20 | private ForkerWrapper wrapper;
21 |
22 | @Override
23 | public void init(ForkerWrapper wrapper) {
24 | this.wrapper = wrapper;
25 | }
26 |
27 | @Override
28 | public void readConfigFile(File file, List properties) throws IOException {
29 | //
30 | // TODO restart app and/or adjust other configuration on reload
31 | // TODO it shouldnt reload one at a time, it should wait a short while for
32 | // all changes, then reload all configuration files in the same order
33 | // 'properties' should
34 | // be cleared before all are reloaded.
35 |
36 | if (file.getName().endsWith(".js")) {
37 | if (engine == null) {
38 | ScriptEngineManager engineManager = new ScriptEngineManager();
39 | engine = engineManager.getEngineByName("nashorn");
40 | Bindings bindings = engine.createBindings();
41 | bindings.put("wrapper", wrapper);
42 | bindings.put("log", wrapper.getLogger());
43 | if (engine == null)
44 | throw new IOException("Cannot find JavaScript engine. Are you on at least Java 8?");
45 | }
46 | FileReader r = new FileReader(file);
47 | try {
48 | @SuppressWarnings("unchecked")
49 | Map o = (Map) engine.eval(r);
50 | for (Map.Entry en : o.entrySet()) {
51 | if (en.getValue() instanceof Map) {
52 | @SuppressWarnings("unchecked")
53 | Map m = (Map) en.getValue();
54 | for (Map.Entry men : m.entrySet()) {
55 | properties.add(new KeyValuePair(en.getKey(),
56 | men.getValue() == null ? null : String.valueOf(men.getValue())));
57 | }
58 | } else
59 | properties.add(new KeyValuePair(en.getKey(),
60 | en.getValue() == null ? null : String.valueOf(en.getValue())));
61 | }
62 | } catch (ScriptException e) {
63 | throw new IOException("Failed to evaluate configuration script.", e);
64 | }
65 | }
66 |
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/forker-wrapper-plugin-scripts/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module com.sshtools.forker.wrapper.plugin.scripts {
2 | requires transitive com.sshtools.forker.wrapper;
3 | requires java.scripting;
4 | provides com.sshtools.forker.wrapper.WrapperPlugin with com.sshtools.forker.wrapper.plugin.scripts.ScriptWrapperPlugin;
5 | }
--------------------------------------------------------------------------------
/forker-wrapper-plugin-scripts/src/main/resources/META-INF/services/com.sshtools.forker.wrapper.WrapperPlugin:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.wrapper.plugin.scripts.ScriptWrapperPlugin
--------------------------------------------------------------------------------
/forker-wrapper/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | *.classpath
4 | /bin/
5 | /linux/
6 | /logs/
7 | /tmp/
8 |
--------------------------------------------------------------------------------
/forker-wrapper/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | forker-wrapper
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.wst.common.project.facet.core.builder
10 |
11 |
12 |
13 |
14 | org.eclipse.jdt.core.javabuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.validation.validationbuilder
20 |
21 |
22 |
23 |
24 | org.eclipse.m2e.core.maven2Builder
25 |
26 |
27 |
28 |
29 |
30 | org.eclipse.jem.workbench.JavaEMFNature
31 | org.eclipse.wst.common.modulecore.ModuleCoreNature
32 | org.eclipse.jdt.core.javanature
33 | org.eclipse.m2e.core.maven2Nature
34 | org.eclipse.wst.common.project.facet.core.nature
35 |
36 |
37 |
--------------------------------------------------------------------------------
/forker-wrapper/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | forker-wrapper
6 | Forker Wrapper
7 | This module uses Forker almost in reverse, providing a way to 'wrap' a Java process, lowering its privileges (but stiill providing a way for root commands to be run), restarting itself and more.
8 |
9 | com.sshtools
10 | forker
11 | 1.8
12 | ..
13 |
14 |
15 |
16 | src/main/java
17 | src/test/java
18 | target/classes
19 | target/test-classes
20 |
21 |
22 | .
23 | src/main/resources
24 |
25 |
26 |
27 |
28 | .
29 | src/test/resources
30 |
31 |
32 |
33 |
34 | org.codehaus.mojo
35 | exec-maven-plugin
36 | 3.0.0
37 |
38 |
39 | com.sshtools.forker.wrapper.ForkerWrapper
40 | true
41 | true
42 |
43 | --help
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | ${project.groupId}
53 | forker-client
54 | ${project.version}
55 |
56 |
57 | info.picocli
58 | picocli
59 | 4.6.1
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/AbstractWrapper.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper;
2 |
3 | import java.awt.SplashScreen;
4 | import java.util.logging.Handler;
5 | import java.util.logging.Level;
6 | import java.util.logging.Logger;
7 |
8 | public class AbstractWrapper {
9 |
10 |
11 | /** The logger. */
12 | protected Logger logger = Logger.getGlobal();
13 |
14 | /**
15 | * Sets the log level.
16 | *
17 | * @param lvl the new log level
18 | */
19 | public void setLogLevel(String lvl) {
20 | setLogLevel(Level.parse(lvl));
21 | }
22 |
23 | /**
24 | * Gets the logger.
25 | *
26 | * @return the logger
27 | */
28 | public Logger getLogger() {
29 | return logger;
30 | }
31 |
32 | /**
33 | * Sets the log level.
34 | *
35 | * @param lvl the new log level
36 | */
37 | public void setLogLevel(Level lvl) {
38 | Logger logger = this.logger;
39 | do {
40 | logger.setLevel(lvl);
41 | for (Handler h : logger.getHandlers()) {
42 | h.setLevel(lvl);
43 | }
44 | logger = logger.getParent();
45 | } while (logger != null);
46 | }
47 |
48 | public void closeSplash() {
49 | try {
50 | final SplashScreen splash = SplashScreen.getSplashScreen();
51 | if(splash != null)
52 | splash.close();
53 | }
54 | catch(Exception e) {
55 | logger.log(Level.FINE, "Ignoring splash error.", e);
56 | }
57 | }
58 |
59 | /**
60 | * Reconfigure logging.
61 | */
62 | protected void reconfigureLogging(String levelName) {
63 | Level lvl = Level.parse(levelName);
64 | setLogLevel(lvl);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/ArgMode.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper;
2 |
3 | public enum ArgMode {
4 | FORCE, APPEND, PREPEND, DEFAULT
5 | }
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/ArgfileMode.java:
--------------------------------------------------------------------------------
1 |
2 | package com.sshtools.forker.wrapper;
3 | public enum ArgfileMode {
4 | COMPACT, ARGFILE, EXPANDED
5 | }
6 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/Argument.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper;
2 |
3 | /**
4 | * The Class Argument.
5 | */
6 | public class Argument {
7 |
8 | /** The type. */
9 | private ArgumentType type;
10 |
11 | /** The value. */
12 | private String value;
13 |
14 | /**
15 | * Instantiates a new argument.
16 | *
17 | * @param value the value
18 | */
19 | public Argument(String value) {
20 | this(ArgumentType.OPTION, value);
21 | }
22 |
23 | /**
24 | * Instantiates a new argument.
25 | *
26 | * @param type the type
27 | * @param value the value
28 | */
29 | public Argument(ArgumentType type, String value) {
30 | this.type = type;
31 | this.value = value;
32 | }
33 |
34 | /**
35 | * Type.
36 | *
37 | * @return the argument type
38 | */
39 | public ArgumentType type() {
40 | return type;
41 | }
42 |
43 | /**
44 | * To arg file line.
45 | *
46 | * @return the string
47 | */
48 | public String toArgFileLine() {
49 | switch (type) {
50 | case QUOTED:
51 | return quote(value, true);
52 | case VALUED_OPTION:
53 | int idx = value.indexOf('=');
54 | if (idx == -1)
55 | return quote(value, true);
56 | else
57 | return value.substring(0, idx + 1) + quote(value.substring(idx + 1), true);
58 | case VALUED_EXTENDED_OPTION:
59 | idx = value.indexOf(':');
60 | if (idx == -1)
61 | return quote(value, true);
62 | else
63 | return value.substring(0, idx + 1) + quote(value.substring(idx + 1), true);
64 | default:
65 | return value;
66 | }
67 | }
68 |
69 | /**
70 | * Quote.
71 | *
72 | * @param value the value
73 | * @param escapeBackslashes the escape backslashes
74 | * @return the string
75 | */
76 | protected String quote(String value, boolean escapeBackslashes) {
77 | if (hasWhitespace(value)) {
78 | return "\"" + value.replace("\\", "\\\\") + "\"";
79 | } else
80 | return value;
81 | }
82 |
83 | /**
84 | * Checks for whitespace.
85 | *
86 | * @param v the v
87 | * @return true, if successful
88 | */
89 | protected boolean hasWhitespace(String v) {
90 | return v.contains(" ") || v.contains("\t") || v.contains("\n") || v.contains("\r");
91 | }
92 |
93 | /**
94 | * To process build argument.
95 | *
96 | * @return the string
97 | */
98 | public String toProcessBuildArgument() {
99 | /*
100 | * ProcessBuilder type arguments (i.e. as added to the List arguments)
101 | * are added pretty much as is, except for VALUED_OPTION.
102 | *
103 | * TODO Check this is actually needed for VALUED_OPTION and this processing
104 | * wasnt added just for argfile
105 | */
106 | if (type == ArgumentType.VALUED_OPTION) {
107 | int idx = value.indexOf('=');
108 | if (idx == -1)
109 | return quote(value, false);
110 | else
111 | return value.substring(0, idx + 1) + quote(value.substring(idx + 1), false);
112 | } else
113 | return value;
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/ArgumentType.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper;
2 |
3 | /**
4 | * The Enum ArgumentType.
5 | */
6 | public enum ArgumentType {
7 |
8 | /** The option. */
9 | OPTION,
10 | /** The quoted. */
11 | QUOTED,
12 | /** The valued option. */
13 | VALUED_OPTION,
14 | /** The valued extended option. */
15 | VALUED_EXTENDED_OPTION
16 | }
17 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/DisplayMode.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper;
2 |
3 | public enum DisplayMode {
4 | CONSOLE, GUI
5 | }
6 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/ForkerWrapperMXBean.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.wrapper;
17 |
18 | public interface ForkerWrapperMXBean {
19 | void ready();
20 |
21 | void wrapped(String jmxUrl);
22 |
23 | String getClassname();
24 |
25 | String getModule();
26 |
27 | String[] getArguments();
28 |
29 | void restart() throws InterruptedException;
30 |
31 | void restart(boolean wait) throws InterruptedException;
32 |
33 | void stop() throws InterruptedException;
34 |
35 | void stop(boolean wait) throws InterruptedException;
36 |
37 | void setLogLevel(String lvl);
38 |
39 | void ping();
40 | }
41 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/KeyValuePair.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper;
2 |
3 | public class KeyValuePair {
4 | String key;
5 | String value;
6 | private boolean bool;
7 |
8 | public KeyValuePair(String line) {
9 | key = line;
10 | int idx = line.indexOf('=');
11 | int spcidx = line.indexOf(' ');
12 | if (spcidx != -1 && (spcidx < idx || idx == -1)) {
13 | idx = spcidx;
14 | }
15 | if (idx != -1) {
16 | value = line.substring(idx + 1).trim();
17 | key = line.substring(0, idx);
18 | } else {
19 | bool = true;
20 | }
21 | }
22 |
23 | public KeyValuePair(String key, String value) {
24 | this.key = key;
25 | this.value = value;
26 | }
27 |
28 | public String getName() {
29 | return key;
30 | }
31 |
32 | public String getValue() {
33 | return value;
34 | }
35 |
36 | public boolean isBool() {
37 | return bool;
38 | }
39 |
40 | public void setValue(String value) {
41 | this.value = value;
42 | }
43 |
44 | @Override
45 | public String toString() {
46 | return "KeyValuePair [key=" + key + ", value=" + value + ", bool=" + bool + "]";
47 | }
48 | }
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/SinkOutputStream.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 |
6 | public class SinkOutputStream extends OutputStream {
7 | @Override
8 | public void write(int b) throws IOException {
9 | }
10 |
11 | @Override
12 | public void write(byte[] b) throws IOException {
13 | }
14 |
15 | @Override
16 | public void write(byte[] b, int off, int len) throws IOException {
17 | }
18 |
19 | @Override
20 | public void flush() throws IOException {
21 | }
22 |
23 | @Override
24 | public void close() throws IOException {
25 | }
26 | }
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/WrapperIO.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.sshtools.forker.wrapper;
17 |
18 | import com.sshtools.forker.client.ForkerBuilder;
19 | import com.sshtools.forker.common.IO;
20 | import com.sshtools.forker.common.IO.DefaultIO;
21 |
22 | /**
23 | * I/O mode that allows executed or Java classes processes to be wrapped in
24 | * {@link ForkerWrapper}. *
25 | */
26 | public class WrapperIO extends DefaultIO {
27 |
28 | /**
29 | * I/O mode to use to obtain a {@link Process} that is wrapped in
30 | * {@link ForkerWrapper} using the standard {@link ForkerBuilder}.
31 | */
32 | public static final IO WRAPPER = DefaultIO.valueOf("WRAPPER");
33 |
34 | public WrapperIO() {
35 | super("WRAPPER", true, false);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/WrapperPlugin.java:
--------------------------------------------------------------------------------
1 | package com.sshtools.forker.wrapper;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.List;
6 |
7 | import com.sshtools.forker.client.ForkerBuilder;
8 |
9 | import picocli.CommandLine.Model.CommandSpec;
10 |
11 | public interface WrapperPlugin {
12 |
13 | default void init(ForkerWrapper wrapper) throws Exception {
14 | }
15 |
16 | default void readConfigFile(File file, List properties) throws IOException {
17 | }
18 |
19 | default boolean event(String name, String cmd, String... args) {
20 | return false;
21 | }
22 |
23 | default boolean buildCommand(ForkerBuilder appBuilder) {
24 | return false;
25 | }
26 |
27 | default int maybeRestart(int retval, int lastRetVal) {
28 | return Integer.MIN_VALUE;
29 | }
30 |
31 | default void addOptions(CommandSpec options) {
32 | }
33 |
34 | default void beforeProcess() throws IOException {
35 | }
36 |
37 | default void start() throws IOException {
38 | }
39 |
40 | default void beforeLaunch() throws IOException {
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/com/sshtools/forker/wrapper/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 - 2021 SSHTOOLS Limited (support@sshtools.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * Provides the classes required to 'wrap' a Java application that it can be
18 | * monitored, restarted, and more. This is all provided by the
19 | * {@link com.sshtools.forker.wrapper.ForkerWrapper} class that may either be
20 | * run stand-alone or embedded into another application. See the documentation
21 | * for this class for more information.
22 | */
23 | package com.sshtools.forker.wrapper;
--------------------------------------------------------------------------------
/forker-wrapper/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Wrapper module
3 | */
4 | module com.sshtools.forker.wrapper {
5 | requires transitive com.sshtools.forker.common;
6 | requires transitive com.sshtools.forker.client;
7 | requires transitive java.logging;
8 | requires java.management;
9 | requires java.management.rmi;
10 | requires transitive info.picocli;
11 | requires static java.desktop;
12 |
13 | exports com.sshtools.forker.wrapper;
14 |
15 | provides com.sshtools.forker.client.ForkerProcessFactory with com.sshtools.forker.wrapper.WrapperProcessFactory;
16 | provides com.sshtools.forker.common.IO with com.sshtools.forker.wrapper.WrapperIO;
17 |
18 | uses com.sshtools.forker.wrapper.WrapperPlugin;
19 | }
--------------------------------------------------------------------------------
/forker-wrapper/src/main/resources/META-INF/services/com.sshtools.forker.client.ForkerProcessFactory:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.wrapper.WrapperProcessFactory
--------------------------------------------------------------------------------
/forker-wrapper/src/main/resources/META-INF/services/com.sshtools.forker.common.IO:
--------------------------------------------------------------------------------
1 | com.sshtools.forker.wrapper.WrapperIO
--------------------------------------------------------------------------------
/src/site/site.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/templates/APACHE-2.txt:
--------------------------------------------------------------------------------
1 | Copyright © ${project.inceptionYear} - ${year} ${owner} (${email})
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
--------------------------------------------------------------------------------