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    }