home *** CD-ROM | disk | FTP | other *** search
- /*
- * KILL version 1.01
- * by George Musser Jr.
- *
- * 11 Jan 87 - initial version
- * 24 Jan 87 - send ACTION_DIE packet to ConsoleTask for Wbench processes
- *
- * Removes a task and as much of its hinterland as possible. We can
- * close windows and unload process code, but screens and other,
- * unidentified structures elude us. We can remove all but 40 or so
- * bytes of an empty CLI.
- *
- * Syntax: KILL <task name>
- * KILL <task pointer>
- *
- * I certainly don't claim that this program is the proper way to deal with
- * the Amiga system. Hopefully, DOS 1.3 will come with a legitimate way to
- * kill runaway and undesirable processes.
- *
- * Things to try in the next version:
- * - improve the killing of Workbench-spawned processes (somehow get
- * at the stdin window via process->pr_ConsoleTask);
- * - try to kill a non-process;
- * - experiment with trackdisk.device (killing the device stops
- * the drive from clicking; perhaps a SleepAmiga utility could
- * kill trackdisk, await a keypress, and Mount the drive);
- * - how does the system associate the CON devices with windows?
- * maybe we can use this to kill devices and Workbench processes
- * more throrughly.
- *
- * Geo's net addresses:
- * CIS 76566,3714 (log on at least biweekly)
- * Plink OHS152 (log on at least monthly)
- * ST801115@BROWNVM.BITNET (log on at least weekly)
- */
-
- /***** Includes ***********************************************************/
-
- #include <exec/types.h>
- #include <exec/nodes.h>
- #include <exec/memory.h>
- #include <exec/tasks.h>
- #include <exec/ports.h>
- #include <exec/execbase.h>
- #include <libraries/dos.h>
- #include <libraries/dosextens.h>
- #include <intuition/intuitionbase.h>
- #include <intuition/intuition.h>
- #include <workbench/startup.h>
- #include <stdio.h>
-
- /***** Defines ************************************************************/
-
- #define LIMIT1_OF_PATIENCE 1 /* Time to wait for death by CLOSEWINDOW */
- #define LIMIT2_OF_PATIENCE 1 /* Time to wait for death by CTRL-C */
- #define LIMIT3_OF_PATIENCE 1 /* Time to wait for console to die */
- #define MAX_WINDOWS 25 /* Maximum number of windows we can close */
-
- /***** Global variables ***************************************************/
-
- struct IntuitionBase *IntuitionBase;
- extern struct DosLibrary *DOSBase;
-
- /***** main() *************************************************************/
-
- main (argc,argv)
- char **argv;
- int argc;
- {
- void exit();
- struct IntuitionBase *OpenLibrary(char *,long);
- int Kill(char *);
- void CloseLibrary();
-
- int rc;
-
- if (argc < 2) /* Not enough arguments, so quit */
- exit (RETURN_ERROR);
-
- IntuitionBase = OpenLibrary("intuition.library",LIBRARY_VERSION);
-
- rc = Kill(*++argv);
-
- if (IntuitionBase)
- CloseLibrary (IntuitionBase);
-
- exit (rc);
- }
-
- /***** Kill() *************************************************************
- *
- * Kill() is a function so that we can experiment with recursion, which
- * may be needed to kill the console task.
- *
- */
-
- int Kill (name)
- char *name;
- {
-
- /***** Functions *******************************************************/
-
- int stch_i();
- void Forbid();
- struct Task *FindTask(char *);
- void Permit();
- void fprintf();
- ULONG LockIBase(long);
- void UnlockIBase(long);
- APTR AllocMem(long,long);
- void PutMsg();
- void Delay(long);
- void CloseLibrary();
- void FreeMem();
- void Signal(struct Task *,long);
- void UnLock(BPTR);
- void UnLoadSeg(BPTR);
- void Close(BPTR);
- struct Message *GetMsg(struct MsgPort *);
- struct MsgPort *FindPort(char *);
- void ReplyMsg();
- void RemTask(struct Task *);
- void ClearMenuStrip(struct Window *);
- void CloseWindow(struct Window *);
-
- /***** Local variables *************************************************/
-
- ULONG ilock; /* Lock on IntuitionBase, so rug stays under us */
-
- struct Task *task; /* This will point to the task we must remove */
-
- struct RootNode *rootnode; /* Central AmigaDOS structure */
- APTR *TaskTable; /* List of processes */
-
- struct Screen *screen; /* Points to a screen */
- struct Window *window; /* ...and to a window */
- int nWindow; /* Number of task's windows */
- struct Window *WindowList[MAX_WINDOWS]; /* Windows opened by task */
-
- unsigned long i; /* Loop counter */
- struct IntuiMessage *message; /* Our forged IntuiMessage */
-
- struct Process *process; /* Oops, task is part of a process */
- struct CommandLineInterface *cli; /* What's more, it's a CLI process */
- BPTR *SegArray; /* A process has a list of segments */
- unsigned long nSeg; /* Number of process segments */
-
- struct MsgPort *myport; /* Reply port for packet */
- struct StandardPacket *packet; /* Message to shut down console */
- struct WBStartup *startup; /* Forged message to Workbench */
-
- struct DOSLibrary *dos; /* So that we can close DOS library */
-
- /***** Here we go ******************************************************/
-
- Forbid();
- if (stch_i(name,&task) < 3) /* If user gave a string, */
- if ((task = FindTask(name)) == NULL) { /* find the corresponding */
- Permit(); /* task. */
- fprintf (stderr,"Can't find `%s'.\n",name);
- return (RETURN_ERROR);
- }
-
- if (task == FindTask(NULL)) { /* No suicide pills here */
- Permit();
- fprintf (stderr,"Can't kill self.\n");
- return (RETURN_ERROR);
- }
-
- #ifdef DEBUG
- Permit();
- printf ("Killing task %lx\n",task);
- Forbid();
- #endif DEBUG
-
- /* Find windows associated with the task */
-
- nWindow = 0;
-
- if (IntuitionBase) {
-
- ilock = LockIBase(0);
-
- /* Loop through screens */
-
- screen = IntuitionBase->FirstScreen;
- while (screen && nWindow < MAX_WINDOWS) {
-
- /* Loop through windows */
-
- window = screen->FirstWindow;
- while (window && nWindow < MAX_WINDOWS) {
-
- /* If this window has an IDCMP, then it points back to
- * the task. Check if this task is the one we want.
- */
-
- if (window->UserPort->mp_SigTask == task)
- WindowList[nWindow++] = window;
-
- window = window->NextWindow;
-
- } /* windows */
-
- screen = screen->NextScreen;
- } /* screens */
-
- UnlockIBase (ilock);
-
- } /* IntuitionBase */
-
- /* First we'll give the task a chance to die quietly. Fake an Intuition
- * CLOSEWINDOW message and send it to an IDCMP equipped to handle such
- * an event. Give the task a second to die and check if it's still
- * around. If so...
- */
-
- if (nWindow) {
-
- message = (struct IntuiMessage *)AllocMem(sizeof(struct IntuiMessage),
- MEMF_PUBLIC);
-
- i = nWindow;
- while (i)
- if (WindowList[--i]->IDCMPFlags & CLOSEWINDOW) {
-
- message->Class = CLOSEWINDOW;
- message->IDCMPWindow = WindowList[i];
- message->ExecMessage.mn_ReplyPort = WindowList[i]->WindowPort;
- message->ExecMessage.mn_Node.ln_Type = NT_MESSAGE;
- message->ExecMessage.mn_Length = sizeof(struct IntuiMessage) -
- sizeof(struct Message);
-
- Permit();
- PutMsg (WindowList[i]->UserPort,message);
-
- Delay (LIMIT1_OF_PATIENCE * TICKS_PER_SECOND);
- Forbid();
-
- FreeMem (message,sizeof(struct IntuiMessage));
-
- if (FindTask(name) != task) {
- Permit();
- return (RETURN_OK);
- }
- }
-
- }
-
- if (task->tc_Node.ln_Type == NT_PROCESS) {
-
- /* Plan B. Try breaking the task with the CTRL-C/CTRL-D combination.
- * Wait a little while and check whether the task is still there.
- * Even if the plan doesn't kill the task, it'll break out of the
- * current CLI command and thereby allow us to close the CLI window.
- */
-
- Permit();
-
- #ifdef DEBUG
- printf ("Sending break signals...");
- #endif DEBUG
-
- Signal (task,SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
- Delay (LIMIT2_OF_PATIENCE * TICKS_PER_SECOND);
- Forbid();
-
- if (FindTask(name) != task) {
- Permit();
- #ifdef DEBUG
- puts ("");
- #endif DEBUG
- return (RETURN_OK);
- }
-
- #ifdef DEBUG
- Permit();
- printf ("done.\n");
- Forbid();
- #endif DEBUG
-
- /* Well, looks as though we'll have to pry it loose by hand. */
-
- process = (struct Process *)task;
- SegArray = (BPTR *)BADDR(process->pr_SegList);
-
- #ifdef DEBUG
- Permit();
- printf ("Killing process %lx\n",&process->pr_MsgPort);
- Forbid();
- #endif DEBUG
-
- /* Delete the process from AmigaDOS's list. First, dig up the master
- * list of processes. Second, scan through the list for our task and
- * reset its pointer to NULL.
- */
-
- rootnode = (struct RootNode *)DOSBase->dl_Root;
- TaskTable = (APTR *)BADDR(rootnode->rn_TaskArray);
-
- for (i = 1; i <= (unsigned long)TaskTable[0]; i++)
- if (TaskTable[i] == (APTR)&process->pr_MsgPort) {
- TaskTable[i] = NULL;
- break;
- }
-
- if (process->pr_TaskNum) {
-
- /* It's not a child of Workbench, so we will have to clean up.
- * First, unlock the current directory. Second, check whether
- * the CLI is in the midst of executing a command. If so,
- * unload the command's code. If not, close the CLI's I/O.
- * Finally, unload the CLI's code.
- */
-
- UnLock (process->pr_CurrentDir);
-
- cli = (struct CommandLineInterface *)BADDR(process->pr_CLI);
-
- if (cli->cli_Module)
- UnLoadSeg (cli->cli_Module);
- else {
-
- /* Close CLI I/O files, if the CLI isn't executing a command.
- * I tried to close cli_StandardInput, et. al., and to check
- * whether any other processes used these file handles as their
- * CLI I/O. These additional measures had no effect. Under
- * the present code, if you kill a CLI from which a background
- * CLI had spawned, AmigaDOS will wait until the background
- * process is done before closing the CLI window.
- *
- * Closing files when the CLI was in the midst of executing a
- * command crashed the system. The CTRL-C/CTRL-D combination
- * above helps.
- */
-
- if (cli->cli_Interactive) {
- if (process->pr_CIS) Close (process->pr_CIS);
- if (process->pr_COS) Close (process->pr_COS);
- }
-
- } /* else cli->cli_Module */
-
- /* Remove process code and table thereof */
-
- nSeg = SegArray[0];
- while (--nSeg > 2)
- UnLoadSeg (SegArray[nSeg]);
- FreeMem (SegArray,SegArray[0] * sizeof(BPTR));
-
- } /* if (process->pr_TaskNum) */
-
- else {
-
- /* Send a death message to the ConsoleTask associated with this
- * process. I'm not sure if this actually does anything, but
- * but it's part of an attempt to close the standard I/O window
- * opened by the startup code.
- */
-
- if (process->pr_ConsoleTask) {
- myport = &((struct Process *)FindPort(NULL))->pr_MsgPort;
-
- packet = (struct StandardPacket *)
- AllocMem(sizeof(struct StandardPacket),MEMF_PUBLIC);
-
- packet->sp_Msg.mn_Node.ln_Type = NT_MESSAGE;
- packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt);
- packet->sp_Msg.mn_ReplyPort = myport;
- packet->sp_Msg.mn_Length = sizeof(struct DosPacket);
- packet->sp_Pkt.dp_Link = &(packet->sp_Msg);
- packet->sp_Pkt.dp_Port = packet->sp_Msg.mn_ReplyPort;
- packet->sp_Pkt.dp_Type = ACTION_DIE;
-
- Permit();
-
- #ifdef DEBUG
- printf ("Sending message %lx to %lx...",
- packet,process->pr_ConsoleTask);
- #endif DEBUG
-
- PutMsg (process->pr_ConsoleTask,packet);
- Delay (LIMIT3_OF_PATIENCE * TICKS_PER_SECOND);
- if (GetMsg(myport) == &packet->sp_Msg)
- FreeMem (packet,sizeof(struct StandardPacket));
-
- #ifdef DEBUG
- printf ("done.\n");
- #endif DEBUG
-
- Forbid();
-
- }
-
- /* We're killing a child of Workbench. Let Workbench mop up the
- * mess. Fake a message to Workbench in reply to its startup
- * message.
- */
-
- startup = (struct WBStartup *)AllocMem(sizeof(struct WBStartup),
- MEMF_PUBLIC);
-
- startup->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
- startup->sm_Message.mn_ReplyPort = FindPort("Workbench");
- startup->sm_Message.mn_Length = sizeof(struct WBStartup) -
- sizeof(struct Message);
- startup->sm_Process = &process->pr_MsgPort;
- startup->sm_Segment = SegArray[3];
- startup->sm_NumArgs = 0;
- startup->sm_ToolWindow = NULL;
- startup->sm_ArgList = NULL;
- Permit();
-
- #ifdef DEBUG
- printf ("Sending message %lx...",startup);
- #endif DEBUG
-
- ReplyMsg (startup);
-
- #ifdef DEBUG
- printf ("done.\n");
- #endif DEBUG
-
- Forbid();
-
- } /* else (process->pr_TaskNum) */
-
- /* Close libraries opened by process */
-
- #ifdef DEBUG
- Permit();
- printf ("Closing DOS...");
- Forbid();
- #endif DEBUG
-
- dos = (struct DOSLibrary *)DOSBase;
- CloseLibrary (dos);
-
- #ifdef DEBUG
- Permit();
- printf ("done.\n");
- Forbid();
- #endif DEBUG
-
- /* At last we can kill the task. Besides pulling the task from
- * Exec's sight, RemTask() seems to free the Task structure, the task
- * stack, and memory that is specified in the task's tc_MemEntry list
- * (verified empirically). For processes, tc_MemEntry covers the
- * Process structure and the process stack.
- */
-
- #ifdef DEBUG
- Permit();
- printf ("Killing task...");
- Forbid();
- #endif DEBUG
-
- RemTask (task);
-
- #ifdef DEBUG
- Permit();
- printf ("done.\n");
- Forbid();
- #endif DEBUG
-
- } /* process */
-
- else {
-
- /* I haven't experimented with non-processes yet. For now, all
- * we do is remove the task from Exec's list.
- */
-
- RemTask (task);
- FreeMem (task,sizeof(struct Task));
-
- }
-
- /* Close windows */
-
- #ifdef DEBUG
- Permit();
- printf ("Closing windows...");
- Forbid();
- #endif
-
- while (nWindow) {
-
- if (WindowList[--nWindow]->MenuStrip)
- ClearMenuStrip (WindowList[nWindow]);
-
- CloseWindow (WindowList[nWindow]);
-
- }
-
- #ifdef DEBUG
- Permit();
- printf ("done.\n");
- Forbid();
- #endif DEBUG
-
- Permit();
-
- return (RETURN_OK);
-
- }
-
-