Source code: linux/Linux.java
1 /*
2 * @(#)Linux.java
3 * Copyright (C) 2001-2003 Tay Hock Keong <tay_ivan@hotmail.com>
4 * This file is part of JD4X.
5 * JD4X is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 * JD4X is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with JD4X; see the file COPYING. If not, write to the Free
15 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17
18 package linux;
19
20 import arch.module.major.JXMajorModule;
21 import arch.module.data.*;
22 import arch.module.icore.*;
23 import arch.message.error.JXError;
24 import arch.message.request.JXRequest;
25 import arch.task.*;
26 import arch.task.supported.*;
27 import java.util.*;
28
29 /**
30 * The JD4X operating system module is known as Linux. Linux is a platform dependent
31 * module that deals with all the platform specific needs of the JD4X desktop. It has
32 * native support for kernel facilties like administration and file system services.
33 * <BR>Note: Some of J2SDK's functions are undependable for the type of services that
34 * JD4X requires and therefore there is a need to re-implement some of them to have
35 * the JD4X system working correctly. An example would be the Runtime.exec() function.
36 * Careful testing has revealed JNI invokation problems that exist when processes
37 * created by this facility tries to re-invoke Java code. This same facility also
38 * causes XSync() to crash the Java threads and results in unpredictable behavior for
39 * native windowing applications.
40 * @version 0.1, 05/07/2003
41 * @see arch.module.major.JXMajorModule
42 * @see arch.message.error.JXError
43 * @see arch.message.request.JXRequest
44 * @see arch.task.LinuxTask
45 * @see arch.task.supported.LinuxSupportedTask
46 * @see libjxlin.c
47 * @since JD4X 1.0
48 * @author Tay Hock Keong
49 */
50
51 public final class Linux extends JXMajorModule{
52
53 /** Table of current running processes */
54 private static volatile Hashtable runPTable;
55
56 /** Table of current running Jvms */
57 private static volatile Hashtable jvmTable;
58
59 /** Fixed java commands */
60 private static final String[] jvmCmd = {"java", "-jar", "-classpath",
61 "appletviewer"};
62
63 /** Fixed java command extendsions */
64 private static final String[] jvmExt = {".jar", ".class"};
65
66 /** Linux ID */
67 public static final int LINUX_ID=5;
68
69 // loading native OS library in static code block.
70 static{
71 System.loadLibrary("jxlin");
72 }
73
74 /**
75 * Fork and execute a new linux process.
76 * @param arg
77 * the command and its arguments.
78 * @return
79 * the specific linux process pid.
80 * @see libjxlin.c
81 */
82 private native int forkExec0(String arg[]);
83
84 /**
85 * kill a linux process.
86 * @param pid
87 * the ID of the process to be terminated.
88 * @see libjxlin.c
89 */
90 private native void kill0(int pid);
91
92 /**
93 * Default constructor that creates a new Windowing module with the
94 * default settings.
95 * @param home
96 * the absolute path to the user's home directory.
97 */
98 public Linux(){
99 super(); //refactor to this() when other constructors are up!
100 setModuleID(LINUX_ID);
101 setVersion(0.1F);
102 }
103
104 /**
105 * It requests Linux to perform a certain task on another's behalf. The
106 * task requested must be supported before requesting the task.
107 * <BR>Note: Peer methods run under commit are executed in a seperate
108 * thread in JUNK and will not block the other modules. Some peer
109 * functions here are infinite loops and cannot be directly called.
110 * @param task
111 * the task that the requester wants the provider to do.
112 * @return
113 * the error that occured, if any, wrap in a JXError object. null
114 * is returned if no error occured.
115 * <dt><b>Precondition:</b><dd>
116 * Linux ensures that the security manager has granted the
117 * permission to perform the act, else the task must be denied.
118 * <dt><b>Postcondition:</b><dd>
119 * Linux completes the task assigned to it. Any error that
120 * occured while performing the task will be managed under the
121 * thread that executed the task.
122 * @see arch.task.LinuxTask
123 * @see arch.task.supported.LinuxSupportedTask
124 */
125 public JXError commit(JXTask task){
126 JXError reply = null;
127 if(task instanceof LinuxTask){
128 LinuxTask lt = (LinuxTask)task;
129 //check custom permissions?
130 int reqID = lt.getTaskID();
131 if(LinuxSupportedTask.isTaskSupported(reqID)){
132 switch(reqID){
133 case LinuxSupportedTask.FORK_EXEC:
134 requestProcess(lt.getCommand());
135 break;
136
137 case LinuxSupportedTask.EXEC_JAR:
138 requestJvmJar(lt.getJvmOption(), lt.getCommand());
139 break;
140
141 case LinuxSupportedTask.EXEC_CLASS:
142 requestJvmClass(lt.getJvmOption(), lt.getFQC());
143 break;
144
145 case LinuxSupportedTask.KILL_JVM:
146 case LinuxSupportedTask.KILL_PROCESS:
147 terminate(reqID, lt.getPIDs());
148 break;
149
150 default: //can only result if reqID is mutable.
151 reply = new JXError("Task ID mutation detected, ID=="+reqID);
152 break;
153 }
154 }
155 else{
156 reply = new JXError("Invalid task type, JXTask!=LinuxTask.");
157 }
158 }
159 return reply;
160 }
161
162 /**
163 * All major modules must provide the facility to configure
164 * its properties such that user customization is possible.
165 * @param prop
166 * the property that defines the new configuration. prop
167 * will be interpreted differently depending on the specific
168 * implementation of the configure method.
169 * @return
170 * the error that occured, if any, wrap in a JXError object. null
171 * is returned if no error occured.
172 * <dt><b>Precondition:</b><dd>
173 * The individual modules must maintain its own default
174 * configuration values that can be restorted on error.
175 * <dt><b>Postcondition:</b><dd>
176 * The module is updated to the new configuration only if
177 * no error ocurred, else it will fall back to the default
178 * configuration values. Any error that resulted from the
179 * new configuration will be managed under the thread that
180 * executed the configuration procedure.
181 */
182 private JXError configure(Object prop){
183 System.out.println("Linux: configure();");
184 return null;
185 }
186
187 /**
188 * Get the list of current running native process IDs.
189 * @return
190 * the names of the running processes.
191 */
192 public static Object[] getProcesses(){
193 if(runPTable != null){
194 synchronized(runPTable){
195 return runPTable.keySet().toArray();
196 }
197 }
198 return null;
199 }
200
201 /**
202 * Get the list of current running Jvm IDs.
203 * @return
204 * the names of the running Jvms.
205 */
206 public static Object[] getJvms(){
207 if(jvmTable != null){
208 synchronized(jvmTable){
209 return jvmTable.keySet().toArray();
210 }
211 }
212 return null;
213 }
214
215 /**
216 * It allows the module developer to provide his/her information. The basic
217 * information should be short and should include the following format each
218 * on its own line:
219 * <BR>Module: Name, version
220 * <BR>Developer: Name, <Email>. Name, <Email>. [...]
221 * @return
222 * the credit information that the developer desires to be displayed.
223 * <dt><b>Postcondition:</b><dd>
224 * The information to be displayed will be added to the global information
225 * panel. The display panel is implementation dependent.
226 */
227 public String getCredits(){
228 String m = toString();
229 m = m.replaceAll("<", "<");
230 m = m.replaceAll(">", ">");
231 StringBuffer temp = new StringBuffer("<BR>Module: ").append(m).
232 append("<BR>Developer: Tay Hock Keong, <tay_ivan@hotmail.com>.");
233 return temp.toString();
234 }
235
236 /**
237 * Initialize all resources and to prepare the module to do useful task.
238 * @return
239 * the error that occured, if any, wrap in a JXError object. null
240 * is returned if no error occured.
241 * <dt><b>Postcondition:</b><dd>
242 * The module is properly initialized such that it can support all
243 * the functionality it is supposed to provide for its clients.
244 * @see arch.message.error.JXError
245 */
246 public JXError init(){
247 //assume max of 30 processes with 0.75 load factor.
248 runPTable = new Hashtable(40);
249 //assume max of 30 processes with 0.75 load factor.
250 jvmTable = new Hashtable(40);
251 return null;
252 }
253
254 /**
255 * All major modules must provide the facility to indicate whether
256 * it is busy processing some command or it is in an idle state.
257 * The client calling this method must be ready to defer any task
258 * that it wants the module to do if the return value is true. If
259 * a module do not have shared resources that requires queueing or
260 * synchronization then this method should always return true.
261 * @return
262 * true=the module is current processing something, else
263 * the module is in its idle state.
264 * <dt><b>Postcondition:</b><dd>
265 * If the return value is false, then the module is ready
266 * to do any task delegated to it, else if the client
267 * forces the busy module to do its requested task, the
268 * action taken by the module is undefined depending
269 * on the different module's implementation.
270 *
271 */
272 public boolean isBusy(){
273 return false;
274 }
275
276 /**
277 * It allows the native code to feed messages back to the standard
278 * output logged by JUNK. Use for debugging and feedback tracking.
279 * This method will print a newline at end of the message. Even
280 * though this method is not a required standard interface but it
281 * is very useful and should be considered by all implementations.
282 * @param msg
283 * the client message to be printed to the GUI.
284 */
285 private static void println(String msg){
286 System.out.println(msg);
287 }
288
289 /**
290 * Start a new linux process.
291 * @param param
292 * the command and parameters to be passed to a
293 * JXProcess.
294 * <dt><b>Postcondition:</b><dd>
295 * An independent native process is started.
296 */
297 private void requestProcess(String[] params){
298 if((params!=null) && (params[0]!=null)){
299 int pid = forkExec0(params);
300 JXProcess p = new JXProcess(pid, params[0]);
301 synchronized(runPTable){
302 runPTable.put(p.getID(), p);
303 }
304 }
305 else{
306 System.out.println("Linux: Process parameter error, param==null.");
307 }
308 }
309
310 /**
311 * Start a new Jvm process using jar invokation.
312 * @param jvmOpts
313 * the desired Jvm options.
314 * @param cmds
315 * the jar file path and arguments.
316 * <dt><b>Postcondition:</b><dd>
317 * An independent Jvm process is started.
318 */
319 private void requestJvmJar(String[] jvmOpts, String[] cmds){
320 String[] argv = null;
321 int k = 1;
322
323 if((cmds!=null) && (cmds[0]!=null)){
324 if(cmds[0].endsWith(jvmExt[0])){ /* jar file. */
325 if((jvmOpts!=null) && (jvmOpts[0]!=null)){
326 argv = new String[cmds.length+jvmOpts.length+2];
327 for(int i=0; i<jvmOpts.length; i++){
328 argv[k++] = jvmOpts[i];
329 }
330 }
331 else{
332 argv = new String[cmds.length+2];
333 }
334 argv[0] = jvmCmd[0];
335 argv[k++] = jvmCmd[1];
336 for(int i=0; i<cmds.length; i++){
337 argv[k++] = cmds[i];
338 }
339 int pid = forkExec0(argv);
340 JXProcess p = new JXProcess(pid, cmds[0]);
341 synchronized(jvmTable){
342 jvmTable.put(p.getID(), p);
343 }
344 }
345 else{
346 System.out.println("Linux: Jvm jar file parameter error.");
347 }
348 }
349 else{
350 System.out.println("Linux: Jvm jar command error.");
351 }
352 }
353
354 /**
355 * Start a new Jvm process using class invokation.
356 * @param jvmOpts
357 * the desired Jvm options.
358 * @param cmds
359 * the fqc path and arguments.
360 * <dt><b>Postcondition:</b><dd>
361 * An independent Jvm process is started.
362 */
363 private void requestJvmClass(String[] jvmOpts, String[] cmds){
364 String[] argv = null;
365 int k = 1;
366
367 if((cmds!=null) && (cmds[0]!=null)){
368 if((jvmOpts!=null) && (jvmOpts[0]!=null)){
369 argv = new String[cmds.length+jvmOpts.length+1];
370 for(int i=0; i<jvmOpts.length; i++){
371 argv[k++] = jvmOpts[i];
372 }
373 }
374 else{
375 argv = new String[cmds.length+1];
376 }
377 argv[0] = jvmCmd[0];
378 for(int i=0; i<cmds.length; i++){
379 argv[k++] = cmds[i];
380 }
381 int pid = forkExec0(argv);
382 JXProcess p = new JXProcess(pid, cmds[0]);
383 synchronized(jvmTable){
384 jvmTable.put(p.getID(), p);
385 }
386 }
387 else{
388 System.out.println("Linux: Jvm class command error.");
389 }
390 }
391
392 /**
393 * All major modules must provide the facility to restore
394 * the previous state of the module such that it can retain
395 * previous module configurations and preferences.
396 * @return
397 * the restore status error that occured, if any, wrap in
398 * a JXError object. null is returned if no error occured.
399 * <dt><b>Precondition:</b><dd>
400 * The previous state of the module must have already been
401 * saved into persistent storage.
402 * <dt><b>Postcondition:</b><dd>
403 * The module is restored to its previous state according
404 * to the saved data. If any error occured during the
405 * restoration or if no saved data is available, a default
406 * state must be provided by the module as a backup.
407 */
408 public JXError restoreState(){
409 System.out.println("Linux: restoreState();");
410 return null;
411 }
412
413 /**
414 * All major modules must provide the facility to save the
415 * state of the module such that it can be restorted when
416 * the previous state of the module is desired.
417 * @return
418 * the save status error that occured, if any, wrap in a
419 * JXError object. null is returned if no error occured.
420 * <dt><b>Postcondition:</b><dd>
421 * All configuration, user preference and module component
422 * states are place into persistent storage such that
423 * different execution instances can use the common data.
424 */
425 public JXError saveState(){
426 System.out.println("Linux: saveState();");
427 return null;
428 }
429
430 /**
431 * Terminate the module, clean up and exit native code.
432 * @return
433 * the error that occured, if any, wrap in a JXError object. null
434 * is returned if no error occured.
435 * @see arch.message.error.JXError
436 */
437 public JXError shutdown(){
438 //Jvm shutdown
439 terminate(LinuxSupportedTask.KILL_JVM, jvmTable.keySet().toArray());
440 //process shutdown
441 terminate(LinuxSupportedTask.KILL_PROCESS, runPTable.keySet().toArray());
442 runPTable.clear();
443 jvmTable.clear();
444 return null;
445 }
446
447 /**
448 * Allow the terminating of any task that Linux has been requested to perform.
449 * Tasks that are waiting to be executed or is already being executed will
450 * be halted and removed from Linux's task list. A terminiated task cannot
451 * be restarted or referenced again.
452 * @param id
453 * the type of task/tasks to be terminated.
454 * @param param
455 * the task ID/IDs that is/are being requested for termination.
456 */
457 private void terminate(int id, Object[] param){
458 JXError e = null;
459 JXProcess p = null;
460
461 for(int i=0; i<param.length; i++){
462 if(id == LinuxSupportedTask.KILL_PROCESS){
463 synchronized(runPTable){
464 p = (JXProcess)runPTable.remove(param[i]);
465 }
466 }
467 else if(id == LinuxSupportedTask.KILL_JVM){
468 synchronized(jvmTable){
469 p = (JXProcess)jvmTable.remove(param[i]);
470 }
471 }
472 if(p != null){
473 kill0(p.getID().intValue());
474 }
475 else{
476 System.out.println("Linux: Unknown termination process "+p.toString());
477 }
478 }
479 }
480
481 /**
482 * A String representation of the core module. The exact details of the
483 * representation are unspecified and subject to change, but the following
484 * maybe regarded as typical: JD4X <m5-v0.1>, [mj] LINUX.
485 * It can be interpreted as module id = 5, version = 0.1 and a major module
486 * known as Linux.
487 * @return
488 * the string representation of Linux with its module status.
489 * @see arch.module.major.JXMajorModule
490 */
491 public String toString(){
492 StringBuffer temp = new StringBuffer(super.toString()).append(" LINUX");
493 return temp.toString();
494 }
495
496 /**
497 * Testing function.
498 */
499 public static void main(String[] argv){
500 String[] cargv = {"evim", "/home/o27/error"};
501 Linux l = new Linux();
502 l.init();
503 JXTask task = LinuxSupportedTask.getTaskDescription(LinuxSupportedTask.FORK_EXEC);
504 ((LinuxTask)task).setCommand(cargv);
505 l.commit(task);
506 }
507 }