001 // This file is part of the program FRYSK. 002 // 003 // Copyright 2005, 2006, 2007, 2008, Red Hat Inc. 004 // 005 // FRYSK is free software; you can redistribute it and/or modify it 006 // under the terms of the GNU General Public License as published by 007 // the Free Software Foundation; version 2 of the License. 008 // 009 // FRYSK is distributed in the hope that it will be useful, but 010 // WITHOUT ANY WARRANTY; without even the implied warranty of 011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 // General Public License for more details. 013 // 014 // You should have received a copy of the GNU General Public License 015 // along with FRYSK; if not, write to the Free Software Foundation, 016 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 017 // 018 // In addition, as a special exception, Red Hat, Inc. gives You the 019 // additional right to link the code of FRYSK with code not covered 020 // under the GNU General Public License ("Non-GPL Code") and to 021 // distribute linked combinations including the two, subject to the 022 // limitations in this paragraph. Non-GPL Code permitted under this 023 // exception must only link to the code of FRYSK through those well 024 // defined interfaces identified in the file named EXCEPTION found in 025 // the source code files (the "Approved Interfaces"). The files of 026 // Non-GPL Code may instantiate templates or use macros or inline 027 // functions from the Approved Interfaces without causing the 028 // resulting work to be covered by the GNU General Public 029 // License. Only Red Hat, Inc. may make changes or additions to the 030 // list of Approved Interfaces. You must obey the GNU General Public 031 // License in all respects for all of the FRYSK code and other code 032 // used in conjunction with FRYSK except the Non-GPL Code covered by 033 // this exception. If you modify this file, you may extend this 034 // exception to your version of the file, but you are not obligated to 035 // do so. If you do not wish to provide this exception without 036 // modification, you must delete this exception statement from your 037 // version and license this file solely under the GPL without 038 // exception. 039 040 package frysk.proc.live; 041 042 import java.util.Iterator; 043 import java.util.Collection; 044 import java.util.HashSet; 045 import frysk.rsl.Log; 046 import frysk.rsl.LogFactory; 047 import frysk.proc.Observation; 048 049 /** 050 * A UNIX Process State 051 * 052 */ 053 054 abstract class LinuxPtraceProcState extends State { 055 private static final Log fine = LogFactory.fine(LinuxPtraceProcState.class); 056 private static final Log finest = LogFactory.finest(LinuxPtraceProcState.class); 057 058 LinuxPtraceProcState(String state) { 059 super (state); 060 } 061 LinuxPtraceProcState handleRemoval(LinuxPtraceProc proc) { 062 throw unhandled(proc, "handleRemoval"); 063 } 064 LinuxPtraceProcState handleTaskAttachCompleted(LinuxPtraceProc proc, LinuxPtraceTask task) { 065 throw unhandled(proc, "handleTaskAttachCompleted"); 066 } 067 LinuxPtraceProcState handleTaskDetachCompleted(LinuxPtraceProc proc, LinuxPtraceTask task) { 068 throw unhandled(proc, "handleTaskDetachCompleted"); 069 } 070 LinuxPtraceProcState handleTaskDetachCompleted(LinuxPtraceProc proc, LinuxPtraceTask task, LinuxPtraceTask clone) { 071 throw unhandled(proc, "handleTaskDetachCompleted/clone"); } 072 LinuxPtraceProcState handleAddObservation(LinuxPtraceProc proc, Observation observation) { 073 throw unhandled(proc, "handleAddObservation"); 074 } 075 LinuxPtraceProcState handleDeleteObservation(LinuxPtraceProc proc, Observation observation) { 076 throw unhandled(proc, "handleDeleteObservation"); 077 } 078 LinuxPtraceProcState handleDetach(LinuxPtraceProc proc, boolean shouldRemoveObservers) { 079 throw unhandled(proc, "handleDetach"); 080 } 081 082 /** 083 * Return the Proc's initial state. 084 * 085 * A new detached process materializes out of no where by 086 * magically appearing in <tt>/proc</tt>; a new attached process 087 * is always created via fork. The latter, until an observer is 088 * added is assumed to be about to detach. 089 */ 090 static LinuxPtraceProcState initial (boolean starting) { 091 fine.log("initial", starting); 092 if (starting) 093 return new Detaching(); 094 else 095 return detached; 096 } 097 098 /** 099 * The process is running free (or at least was the last time its 100 * status was checked). 101 */ 102 private static final LinuxPtraceProcState detached 103 = new LinuxPtraceProcState ("detached") { 104 LinuxPtraceProcState handleRemoval(LinuxPtraceProc proc) { 105 fine.log("handleRemoval", proc); 106 // XXX: What about a dieing proc's tasks, have a 107 // dieing state and force a proc refresh? 108 if (proc.parent != null) 109 proc.parent.remove (proc); 110 // Take it out of the host's data-base. 111 ((LinuxPtraceHost)proc.getHost()).removeProc(proc); 112 return destroyed; 113 } 114 LinuxPtraceProcState handleAddObservation(LinuxPtraceProc proc, 115 Observation observation) { 116 fine.log("handleAddObserver", proc); 117 return Attaching.initialState (proc, observation); 118 } 119 LinuxPtraceProcState handleDeleteObservation(LinuxPtraceProc proc, 120 Observation observation) { 121 fine.log("handleDeleteObservation", proc); 122 // Must be bogus; if there were observations then the 123 // Proc wouldn't be in this state. 124 observation.fail (new RuntimeException ("not attached")); 125 return detached; 126 } 127 }; 128 129 /** 130 * A process is being attached, this is broken down into 131 * sub-states. 132 */ 133 private static class Attaching 134 { 135 /** 136 * The initial attaching state, find the main task and tell it 137 * to attach. 138 */ 139 static LinuxPtraceProcState initialState (LinuxPtraceProc proc, Observation observation) 140 { 141 fine.log("state", proc); 142 if (!proc.addObservation (observation)) 143 observation.fail(new RuntimeException("not actually added")); 144 // Grab the main task; only bother with the refresh if the 145 // Proc has no clue as to its task list. 146 if (proc.getTasks().size() == 0) 147 proc.sendRefresh (); 148 // Assumes that the main Task's ID == the Proc's ID. 149 LinuxPtraceTask mainTask 150 = ((LinuxPtraceHost)proc.getHost()).getTask(proc.pid); 151 if (mainTask == null) { 152 // The main task exited and a refresh managed to 153 // update Proc removing it. 154 observation.fail (new RuntimeException ("main task exited")); 155 return detached; 156 } 157 // Tell the main task to get things started. 158 mainTask.performAttach (); 159 return new Attaching.ToMainTask (mainTask); 160 } 161 /** 162 * All tasks attached, set them running and notify any 163 * interested parties. 164 */ 165 private static LinuxPtraceProcState allAttached (LinuxPtraceProc proc) 166 { 167 fine.log("allAttached", proc); 168 for (Iterator i = proc.observationsIterator(); 169 i.hasNext ();) { 170 Observation observation = (Observation) i.next (); 171 observation.handleAdd (); 172 } 173 // .., let them go, and mark this as 174 // attached/running. 175 for (Iterator i = proc.getTasks ().iterator (); 176 i.hasNext (); ) { 177 LinuxPtraceTask t = (LinuxPtraceTask) i.next (); 178 t.performContinue (); 179 } 180 proc.observableAttachedXXX.notify (proc); 181 return running; 182 } 183 /** 184 * In the process of attaching, the main task has been sent an 185 * attach request. That task stopping indicates that the 186 * entire process is stopped and hence all the other tasks can 187 * be attached. 188 */ 189 private static class ToMainTask extends LinuxPtraceProcState { 190 private LinuxPtraceTask mainTask; 191 ToMainTask (LinuxPtraceTask mainTask) 192 { 193 super ("Attaching.ToMainTask"); 194 this.mainTask = mainTask; 195 } 196 LinuxPtraceProcState handleTaskAttachCompleted (LinuxPtraceProc proc, LinuxPtraceTask task) 197 { 198 fine.log("handleTaskAttachCompleted", proc); 199 // With the main task attached, it is possible to get 200 // an up-to-date list of tasks. Remove from it the 201 // tasks already being attached. Ask them to also 202 // attach. 203 proc.sendRefresh (); 204 Collection attachingTasks = proc.getTasks (); 205 attachingTasks.remove (task); 206 // Attach to those remaining attaching tasks. 207 for (Iterator i = attachingTasks.iterator (); 208 i.hasNext (); ) { 209 LinuxPtraceTask t = (LinuxPtraceTask) i.next (); 210 t.performAttach (); 211 } 212 // What next? Nothing else wait for all the attaching 213 // tasks. 214 if (attachingTasks.size () == 0) 215 return allAttached (proc); 216 else 217 return new Attaching.ToOtherTasks (attachingTasks); 218 219 } 220 LinuxPtraceProcState handleAddObservation (LinuxPtraceProc proc, 221 Observation observation) 222 { 223 fine.log("handleAddObservation", proc); 224 // An extra observation, just add it to the list. 225 proc.addObservation (observation); 226 return this; 227 } 228 LinuxPtraceProcState handleDeleteObservation (LinuxPtraceProc proc, 229 Observation observation) 230 { 231 fine.log("handleDeleteObservation", proc); 232 // If the observation was never added, this will 233 // return false, but that is ok. 234 proc.removeObservation (observation); 235 observation.fail (new RuntimeException ("canceled")); 236 if (proc.observationsSize() == 0) { 237 // None of the other tasks are attached, just need 238 // to detach the main one. 239 mainTask.performDetach (false); 240 return new Detaching (proc, mainTask); 241 } 242 return this; 243 } 244 245 LinuxPtraceProcState handleTaskDetachCompleted (LinuxPtraceProc proc, LinuxPtraceTask task) 246 { 247 return this; 248 } 249 250 LinuxPtraceProcState handleDetach(LinuxPtraceProc proc, boolean shouldRemoveObservers) 251 { 252 fine.log("handleDetach", proc); 253 return new Detaching (proc, shouldRemoveObservers); 254 } 255 256 } 257 /** 258 * In the process of attaching, the main task is attached, now 259 * waiting for the remaining tasks to indicate they, too, have 260 * attached (or at least processed the attach request). 261 */ 262 static private class ToOtherTasks extends LinuxPtraceProcState { 263 private Collection attachingTasks; 264 ToOtherTasks (Collection attachingTasks) 265 { 266 super ("Attaching.ToOtherTasks"); 267 this.attachingTasks = attachingTasks; 268 } 269 LinuxPtraceProcState handleTaskAttachCompleted (LinuxPtraceProc proc, LinuxPtraceTask task) 270 { 271 fine.log("handleTaskAttachCompleted", proc); 272 // As each task reports that it has been attached, 273 // remove it from the pool, wait until there are none 274 // left. 275 attachingTasks.remove (task); 276 if (attachingTasks.size () == 0) 277 return allAttached (proc); 278 return this; 279 } 280 LinuxPtraceProcState handleAddObservation (LinuxPtraceProc proc, 281 Observation observation) 282 { 283 fine.log("handleAddObservation", proc); 284 proc.addObservation (observation); 285 return this; 286 } 287 LinuxPtraceProcState handleDeleteObservation (LinuxPtraceProc proc, 288 Observation observation) 289 { 290 fine.log("handleDeleteObservation", proc); 291 proc.removeObservation (observation); 292 observation.fail (new RuntimeException ("canceled")); 293 if (proc.observationsSize () == 0) 294 return new Detaching (proc, false); 295 return this; 296 } 297 } 298 } 299 300 /** 301 * In the process of detaching; waiting for all tasks to report 302 * back that they have successfully detached. 303 */ 304 private static class Detaching extends LinuxPtraceProcState { 305 private Collection attachedTasks; 306 /** 307 * A Proc just created using fork; task yet to be created and 308 * want to detach from it. 309 */ 310 Detaching() { 311 super("Detaching"); 312 attachedTasks = new HashSet(); 313 } 314 /** 315 * Start detaching the entire process. 316 * @param shouldRemoveObservers whether the observers on each task should 317 * be removed. 318 */ 319 Detaching (LinuxPtraceProc proc, boolean shouldRemoveObservers) 320 { 321 super ("Detaching"); 322 attachedTasks = proc.getTasks (); 323 for (Iterator i = attachedTasks.iterator (); 324 i.hasNext (); ) { 325 LinuxPtraceTask t = (LinuxPtraceTask) i.next (); 326 t.performDetach (shouldRemoveObservers); 327 } 328 } 329 /** 330 * Either just starting up and assumed to be detaching or just 331 * attached to the main task. 332 */ 333 Detaching (LinuxPtraceProc proc, LinuxPtraceTask mainTask) 334 { 335 super ("Detaching"); 336 attachedTasks = new HashSet (); 337 attachedTasks.add (mainTask); 338 } 339 LinuxPtraceProcState handleTaskDetachCompleted (LinuxPtraceProc proc, LinuxPtraceTask task) 340 { 341 fine.log("handleTaskDetachCompleted", proc, "task", task); 342 // As each task reports that it has detached, add it 343 // to the detached list. 344 attachedTasks.remove (task); 345 if (attachedTasks.size () > 0) 346 // Still more tasks to detach. 347 return this; 348 // All done, notify. 349 proc.observableDetachedXXX.notify (proc); 350 return detached; 351 } 352 LinuxPtraceProcState handleTaskDetachCompleted (LinuxPtraceProc proc, LinuxPtraceTask task, 353 LinuxPtraceTask clone) 354 { 355 fine.log("handleTaskDetachCompleted", proc); 356 attachedTasks.remove (task); 357 // Doh, a clone, need to also wait for that to detach. 358 attachedTasks.add (clone); 359 return this; 360 } 361 LinuxPtraceProcState handleDetach(LinuxPtraceProc proc, boolean shouldRemoveObservers) 362 { 363 //Already detaching, don't have to do anything different. 364 if (shouldRemoveObservers) 365 return this; 366 367 return super.handleDetach(proc, shouldRemoveObservers); 368 } 369 LinuxPtraceProcState handleAddObservation (LinuxPtraceProc proc, 370 Observation observation) 371 { 372 fine.log("handleAddObservation", proc); 373 // Ulgh, detaching and a new observer arrived. 374 return Attaching.initialState (proc, observation); 375 } 376 LinuxPtraceProcState handleDeleteObservation (LinuxPtraceProc proc, 377 Observation observation) 378 { 379 fine.log("handleDeleteObservation", proc); 380 // Ouch; request to remove what must be an already 381 // removed observation. 382 observation.fail (new RuntimeException ("canceled")); 383 return this; 384 } 385 } 386 387 /** 388 * The process has been destroyed. 389 */ 390 private static final LinuxPtraceProcState destroyed = new LinuxPtraceProcState ("destroyed") 391 { 392 }; 393 394 /** 395 * The process is running, add and remove observers. If the 396 * number of observers ever reaches zero, start detaching. 397 */ 398 private static final LinuxPtraceProcState running = new LinuxPtraceProcState ("running") 399 { 400 LinuxPtraceProcState handleAddObservation (LinuxPtraceProc proc, 401 Observation observation) 402 { 403 fine.log("handleAddObservation", proc); 404 proc.addObservation(observation); 405 observation.handleAdd (); 406 return running; 407 } 408 LinuxPtraceProcState handleDeleteObservation (LinuxPtraceProc proc, 409 Observation observation) 410 { 411 fine.log("handleDeleteObservation", proc); 412 if (proc.removeObservation (observation)) { 413 finest.log("handleDeleteObservation remove succeeded"); 414 observation.handleDelete (); 415 if (proc.observationsSize () == 0) { 416 finest.log("handleDeleteObservation size == 0, detaching"); 417 return new Detaching (proc, false); 418 } 419 } 420 else 421 observation.fail (new RuntimeException ("not added")); 422 423 return running; 424 } 425 LinuxPtraceProcState handleDetach(LinuxPtraceProc proc, boolean shouldRemoveObservers) 426 { 427 fine.log("handleDetach", proc); 428 return new Detaching (proc, shouldRemoveObservers); 429 } 430 }; 431 }