home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file forms part of "TKERN" - "Troy's Kernel for Windows".
- *
- * Copyright (C) 1994 Troy Rollo <troy@cbme.unsw.EDU.AU>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- /*
- * This module handles task and process management.
- *
- * Tasks are Windows objects, identified by a task handle. Task handles
- * can be reused without us waiting for them, so we can't use them to
- * track parent-child relationships or to impliment the wait calls.
- *
- * Processes are tkern only objects which overcome these limitations.
- * Every task is assigned a process number when it enters the system.
- * When it leaves the system, if its parent is a tkern process, and
- * it was created from tkern_exec, a zombie process is created, for which
- * the parent must wait.
- */
-
- #include <stdio.h>
-
- #include <windows.h>
- #include <toolhelp.h>
- #include <stdlib.h>
- #include <memory.h>
- #include <string.h>
- #include <alloc.h>
- #include <stdarg.h>
- #include <errno.h>
- #include <sys/tfile.h>
- #include <sys/task.h>
- #include <sys/ioctl.h>
- #include <sys/wait.h>
- #include <sys/tkern.h>
-
- extern struct tfile _files[];
-
- struct tk_process _process[N_TKPROCS];
- struct tk_process _zombies[N_TKPROCS];
- struct task _tasks[TNTASK];
-
- int nTasks = 0; // Doesn't count fledgelings
-
- static int nProcesses = 0;
- static int nZombies = 0;
- static struct task *ptNext = 0;
- static struct task *ptLast = 0;
- static struct task *ptParent = 0;
- static HTASK htaskParent = 0;
- static short pidParent = 0;
- static short pidChild = 0;
- static short pidCurrent;
- static short nPIDNext = 3; // The first PID is 2, but that's fixed up
- // in WinMain.
-
- static void task_is_dead( int iTask);
-
- struct task *
- GetTaskInfo(void)
- {
- HTASK hTask;
- int i;
-
- hTask = GetCurrentTask();
- for (i = 0; i < TNTASK; i++)
- {
- if (_tasks[i].hTask == hTask)
- {
- /* If the task is in fledgeling mode,
- * don't deliver signals.
- */
- if (_tasks[i].nSignal &&
- _tasks[i].iFledgeling == -1)
- {
- (*_tasks[i].intrproc)(_tasks[i].nSignal);
- _tasks[i].nSignal = 0;
- }
-
-
- while (_tasks[i].iFledgeling != -1)
- i = _tasks[i].iFledgeling;
- return &_tasks[i];
- }
- }
- if (ptNext)
- {
- /* This should be impossible in newer apps.
- * Older ones may get screwed up by Borland's
- * _setupio routine, which calls isatty, invoking
- * tkern_isatty and causing premature registration.
- *
- * We know this is safe because if we get here, the
- * task has not registered yet, and consequently can
- * not have yielded. We also know that we must be
- * a TKERN app to cause GetTaskInfo() to be called.
- */
- return ptNext;
- }
- for (i = 0; i < TNTASK; i++)
- {
- if (!_tasks[i].hTask)
- {
- _tasks[i].hTask = hTask;
- _tasks[i].pchCreatedBy = *((char **) ((char *) &hTask + 6));
- return &_tasks[i];
- }
- }
- return 0;
- }
-
- void
- inc_pid(short *pid)
- {
- if (*pid == 32767)
- *pid = 2;
- else
- (*pid)++;
- }
-
-
- /*
- * aiExecErrors translates between WinExec errors and errno errors
- */
-
- static int aiExecErrors[] =
- {
- ENOMEM,
- EFAULT,
- ENOENT,
- ENOENT,
- EFAULT,
- ENOEXEC,
- ENOEXEC,
- EFAULT,
- EFAULT,
- EFAULT,
- ENOEXEC,
- ENOEXEC,
- ENOEXEC,
- ENOEXEC,
- ENOEXEC,
- ENOEXEC,
- ETXTBSY,
- ETXTBSY,
- ENOEXEC,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT,
- EFAULT
- };
-
-
-
-
-
-
- /* A normal POSIX exec would require pchPath, vaArgs and vaEnv.
- * TKERN's also takes "pchCmdLine", the original command line.
- * this is used by programs that don't use TKERN, and is the
- * value used for WinExec, with pchPath prepended. If the program
- * uses TKERN, it will call in to register, and then we tell it
- * about its real arguments and environment.
- *
- * Yes, we inherit environment too.
- */
-
- short far _export
- tkern_exec( char const *pchPath,
- va_list vaArgs,
- va_list vaEnv,
- char const *pchCmdLine)
- {
- struct task *pt;
- char *pchCommand;
- int iNew;
-
- pt = GetTaskInfo();
- if (pt->nFlags & TF_EXEC) /* Attempt to recursively exec */
- {
- pt->nError = EAGAIN;
- return -1;
- }
- pt->nFlags |= TF_EXEC;
- Copy_Array(&pt->argv, (char **) vaArgs);
- Copy_Array(&pt->envp, (char **) vaEnv);
- pchCommand = (char *) malloc(strlen(pchPath) + strlen(pchCmdLine) + 2);
- strcpy(pchCommand, pchPath);
- if (pchCmdLine)
- {
- strcat(pchCommand, " ");
- strcat(pchCommand, pchCmdLine);
- }
- while (ptNext)
- GetMessages(pt); /* We only allow one exec at any one time */
- ptNext = pt;
- ptLast = pt;
- for (ptParent = pt;
- ptParent->hTask == HTASK_FLEDGELING;
- ptParent = _tasks + ptParent->iParent);
- htaskParent = ptParent->hTask;
- _tasks[pt->iParent].iFledgeling = -1;
- pt->iParent = 0; /* We don't keep this because the parent
- * may die.
- */
- pidParent = ptParent->pid;
- pidChild = 0;
- if ((iNew = WinExec(pchCommand, SW_SHOW)) < 32)
- {
- ptParent->nError = aiExecErrors[iNew];
- ptParent->iFledgeling = -1;
- ptNext = 0;
- task_is_dead(pt - _tasks);
- htaskParent = 0;
- pt->nFlags &= ~TF_EXEC;
- pt->nError = ENOENT;
- tkern_wakeup_call();
- return -1;
- }
- free(pchCommand);
- ptParent->nChildren++;
- ptNext = 0;
- htaskParent = 0;
- tkern_wakeup_call();
- pt->nFlags &= ~TF_EXEC;
- if (pt->hTask == HTASK_FLEDGELING)
- {
- ptParent->iFledgeling = -1;
- task_is_dead(pt - _tasks);
- }
- return pidChild;
- }
-
-
-
-
-
-
- /*
- * The three forms of wait.
- *
- * First, the original wait, which waits unconditionally until the child
- * dies.
- *
- * Second, wait3, which takes flags. It is also supposed to return
- * resource usage information but we don't keep that.
- *
- * Third, waitpid, the POSIX wait. This is to be preferred over wait3.
- */
-
- short far _export
- tkern_wait(union wait *wstatus)
- {
- int i;
- short pid;
-
- struct task *pt;
- struct task *ptParent;
-
- pt = GetTaskInfo();
-
- /* Fledgelings cannot be parents */
- for (ptParent = pt;
- ptParent->hTask == HTASK_FLEDGELING;
- ptParent = _tasks + ptParent->iParent);
-
- /* If the parent is childless, return immediately */
- if (!ptParent->nChildren)
- return 0;
-
- /* Wait until the parent has dead children */
- while (!ptParent->nZombies)
- GetMessages(pt);
-
- /* Reap the first dead child we encounter and return its pid */
- for (i = 0; i < nZombies; i++)
- {
- if (_zombies[i].pidParent == ptParent->pid)
- {
- pid = _zombies[i].pid;
- wstatus->w_status = 0;
- wstatus->w_retcode = _zombies[i].nRetCode;
- if (i < nZombies - 1)
- _zombies[i] = _zombies[nZombies - 1];
- nZombies--;
- ptParent->nZombies--;
- ptParent->nChildren--;
- return pid;
- }
- }
- return 0;
- }
-
-
-
-
-
- short far _export
- tkern_wait3(union wait *wstatus,
- int nFlags,
- struct rusage *pZero)
- {
- int i;
- short pid;
- struct task *pt;
- struct task *ptParent;
-
- pt = GetTaskInfo();
-
- if (pZero)
- {
- pt->nError = EINVAL;
- return -1;
- }
-
- /* Fledgelings cannot be parents */
- for (ptParent = pt;
- ptParent->hTask == HTASK_FLEDGELING;
- ptParent = _tasks + ptParent->iParent);
-
- /* If the parent is childless, return immediately */
- if (!ptParent->nChildren)
- return 0;
-
- /* If the WNOHANG flag is supplied, and there are no
- * zombies, return immediately
- */
- if (!ptParent->nZombies && (nFlags & WNOHANG))
- return 0;
-
- /* Since we don't have BSD jobs, we can't use WUNTRACED */
-
- /* Wait until the parent has dead children */
- while (!ptParent->nZombies)
- GetMessages(pt);
-
- /* Reap the first dead child we encounter and return its pid */
- for (i = 0; i < nZombies; i++)
- {
- if (_zombies[i].pidParent == ptParent->pid)
- {
- pid = _zombies[i].pid;
- wstatus->w_status = 0;
- wstatus->w_retcode = _zombies[i].nRetCode;
- if (i < nZombies - 1)
- _zombies[i] = _zombies[nZombies - 1];
- nZombies--;
- ptParent->nZombies--;
- ptParent->nChildren--;
- return pid;
- }
- }
- return 0;
- }
-
-
-
-
-
- short far _export
- tkern_waitpid( int pid,
- union wait *wstatus,
- int nFlags)
- {
- int i;
- struct task *pt;
- struct task *ptParent;
- BOOL bZombie;
- int iEntry;
- struct tk_process *pProc;
- int nLastZombies = -1;
-
- pt = GetTaskInfo();
-
- /* Fledgelings cannot be parents */
- for (ptParent = pt;
- ptParent->hTask == HTASK_FLEDGELING;
- ptParent = _tasks + ptParent->iParent);
-
- /* If the parent is childless, return immediately */
- if (!ptParent->nChildren)
- return 0;
-
- while (1)
- {
- /* Find the process entry for this child */
- if (pid)
- {
- if (ptParent->nZombies != nLastZombies)
- {
- iEntry = -1;
- for (i = 0; iEntry == -1 && i < nProcesses; i++)
- {
- if (_process[i].pid == pid)
- {
- iEntry = i;
- bZombie = FALSE;
- pProc = &_process[i];
- }
- }
- for (i = 0; iEntry == -1 && i < nZombies; i++)
- {
- if (_zombies[i].pid == pid)
- {
- iEntry = i;
- bZombie = TRUE;
- pProc = &_zombies[i];
- }
- }
- if (pProc->pidParent != ptParent->pid)
- {
- pt->nError = EACCES;
- return -1;
- }
- nLastZombies = ptParent->nZombies;
- if (iEntry == -1)
- {
- pt->nError = EFAULT;
- return -1;
- }
- if (!bZombie && (nFlags & WNOHANG))
- return 0;
- if (bZombie)
- {
- wstatus->w_status = 0;
- wstatus->w_retcode = _zombies[iEntry].nRetCode;
- nZombies--;
- if (iEntry < nZombies)
- _zombies[i] = _zombies[nZombies];
- ptParent->nZombies--;
- ptParent->nChildren--;
- return pid;
- }
- }
- else if (!ptParent->nZombies && (nFlags & WNOHANG))
- {
- return 0;
- }
- }
- else if (ptParent->nZombies)
- {
- for (i = 0; i < nZombies; i++)
- {
- if (_zombies[i].pidParent == ptParent->pid)
- {
- pid = _zombies[i].pid;
- wstatus->w_status = 0;
- wstatus->w_retcode = _zombies[i].nRetCode;
- if (i < nZombies - 1)
- _zombies[i] = _zombies[nZombies - 1];
- nZombies--;
- ptParent->nZombies--;
- ptParent->nChildren--;
- return pid;
- }
- }
- }
- else if (nFlags & WNOHANG)
- {
- return 0;
- }
-
- /* Wait until the parent has dead children */
- GetMessages(pt);
- }
- }
-
-
-
-
-
-
-
- /* tkern_fork only handles the internal (to tkern) part of fork().
- * the Throw/Catch functions must be handled in the child program.
- * consequently, fork is represented as a macro.
- */
-
- int far _export
- tkern_fork(void)
- {
- struct task *pt;
- int i, j;
-
- pt = GetTaskInfo();
- for (i = 0; i < TNTASK; i++)
- {
- if (!_tasks[i].hTask)
- break;
- }
- if (i == TNTASK)
- {
- pt->nError = ENOMEM;
- return -1;
- }
- pt->iFledgeling = i;
- _tasks[i].hTask = HTASK_FLEDGELING;
- _tasks[i].iParent = (pt - _tasks);
- memcpy(_tasks[i].files, pt->files, sizeof(_tasks[i].files));
- for (j = 0; j < UFILE_MAX; j++)
- {
- if (_tasks[i].files[j] != -1)
- _files[_tasks[i].files[j]].tf_cnt++;
- }
- return 0; /* When we return, we are in the "child" process */
- }
-
-
- int far _export
- tkern_total_zombies(void)
- {
- return nZombies;
- }
-
- int far _export
- tkern_list_zombies( struct tk_process *pList,
- int nEntries)
- {
-
- if (nZombies < nEntries)
- nEntries = nZombies;
- memcpy(pList, _zombies, sizeof(*pList) * nEntries);
- return nEntries;
- }
-
- /* Note that because tkern_get_process takes an HTASK, it can
- * only return an active process, not a zombied one
- */
- int far _export
- tkern_get_process( HTASK hTask,
- struct tk_process *tk_proc)
- {
- int i;
-
- for (i = 0; i < nProcesses; i++)
- {
- if (_process[i].hTask == hTask)
- {
- *tk_proc = _process[i];
- return _process[i].pid;
- }
- }
- return 0;
- }
-
-
-
- int far tkern_valid_file(int fd);
-
- void far _export
- tkern_register_program( int *argc,
- char ***argv,
- char ***envp)
- {
- int i;
- struct task *pt;
- TASKENTRY te;
-
- TaskFindHandle(&te, GetCurrentTask());
- if (htaskParent)
- {
- /* Because of the way tkern_exec() is written,
- * the only way this can be true is if this
- * is the task spawned from the tkern_exec()
- */
- pt = ptNext;
- pt->hTask = GetCurrentTask();
- for (i = 0; pt->argv[i]; i++);
- *argc = i;
- *argv = pt->argv;
- *envp = pt->envp;
- pt->pid = pidCurrent;
- }
- else
- {
- pt = GetTaskInfo();
- if (pt->argv)
- {
- for (i = 0; *pt->argv; i++);
- *argc = i;
- *argv = pt->argv;
- *envp = pt->envp;
- }
- else
- {
- *argc = 0;
- *argv = 0;
- *envp = 0;
- }
- pt->pid = pidCurrent;
- }
-
- /* We need to flush all messages in the first task because if
- * that task exits without having yielded, TKFMANGR will miss
- * the exit notification.
- */
- if (!nTasks)
- FlushMessages();
- nTasks++;
- while (!hwndManager)
- GetMessages(pt);
- }
-
- static void
- task_is_dead( int iTask)
- {
- int iFile;
-
- if (_tasks[iTask].iFledgeling != -1)
- task_is_dead(_tasks[iTask].iFledgeling);
- for (iFile = 0; iFile < UFILE_MAX; iFile++)
- if (_tasks[iTask].files[iFile] != -1)
- internal_close(iFile, iTask);
- if (_tasks[iTask].argv)
- {
- free(_tasks[iTask].argv);
- _tasks[iTask].argv = 0;
- }
- if (_tasks[iTask].envp)
- {
- free(_tasks[iTask].envp);
- _tasks[iTask].envp = 0;
- }
- _tasks[iTask].hTask = 0;
- _tasks[iTask].intrproc = 0;
- _tasks[iTask].nSignal = 0;
- _tasks[iTask].iFledgeling = -1;
- }
-
- #pragma argsused
- void far _export
- tkern_program_started(HTASK htaskNew)
- {
- short iEntry, i;
- short nPID;
- BOOL bOK;
-
- if (nProcesses == N_TKPROCS) // Should be impossible
- return; // Well what else can we do?
- iEntry = nProcesses++;
- do
- {
- nPID = nPIDNext;
- bOK = TRUE;
- for (i = 0; i < nProcesses; i++)
- {
- if (_process[i].pid == nPID)
- {
- bOK = FALSE;
- break;
- }
- }
- if (bOK)
- {
- for (i = 0; i < nZombies; i++)
- {
- if (_zombies[i].pid == nPID)
- {
- bOK = FALSE;
- break;
- }
- }
- }
- inc_pid(&nPIDNext);
- } while (!bOK);
- _process[iEntry].pid = nPID;
- _process[iEntry].hTask = htaskNew;
- if (ptNext && !pidChild)
- {
- _process[iEntry].pidParent = pidParent;
- _process[iEntry].hTaskParent = htaskParent;
- pidChild = nPID;
- _process[iEntry].iParent = ptParent - _tasks;
- }
- else
- {
- _process[iEntry].pidParent = 1;
- _process[iEntry].hTaskParent = 0;
- _process[iEntry].iParent = -1;
- }
- pidCurrent = nPID;
- }
-
- void far _export
- tkern_program_dead( HTASK htaskCorpse,
- int nRetCode)
- {
- int i;
- short nPID = 0;
-
- /* First, clean up the tkern data on the task, along with
- * all open files.
- */
-
- for (i = 0; i < TNTASK; i++)
- {
- if (_tasks[i].hTask == htaskCorpse)
- {
- task_is_dead(i);
- nTasks--;
- if (!nTasks)
- SendMessage(hwndManager, TKWM_ALLDONE, 0, 0);
- break;
- }
- }
-
- /* Next, look for any children, and the process itself, in the process
- * list.
- */
- for (i = 0; i < nProcesses; i++)
- {
- /* Change any orphaned processes to pidParent = 1, hTaskParent = 0 */
- if (_process[i].hTaskParent == htaskCorpse)
- {
- /* A process has become orphaned */
- _process[i].hTaskParent = 0;
- _process[i].pidParent = 1;
- _process[i].iParent = -1;
- }
-
- if (_process[i].hTask == htaskCorpse)
- {
- /* This is the process entry for this task */
- nPID = _process[i].pid;
-
- /* If there is room in the zombies table, and the process
- * has a living parent, copy the process entry to the zombies
- * table, and issue a wakeup call for any processes waiting
- * for children.
- *
- * note that if the process has no parents, failing to copy
- * it to the zombie table automatically causes it to be reaped.
- */
- if (nZombies < N_TKPROCS && _process[i].iParent != -1)
- {
- _zombies[nZombies] = _process[i];
- _zombies[nZombies].nRetCode = nRetCode;
- _tasks[_process[i].iParent].nZombies++;
- nZombies++;
- tkern_wakeup_call();
- }
-
- nProcesses--;
- if (i != nProcesses)
- {
- _process[i] = _process[nProcesses];
- i--;
- }
- }
- }
-
- /* Search for any zombie children of the current process and reap them */
- for (i = 0; i < nZombies; i++)
- {
- if (_zombies[i].pidParent == nPID)
- {
- nZombies--;
- if (i != nZombies)
- {
- _zombies[i] = _zombies[nZombies];
- i--;
- }
- }
- }
- }
-
-
- void
- process_init(void)
- {
- TASKENTRY te;
- HTASK htaskNow;
- int i, j;
-
- for (i = 0; i < TNTASK; i++)
- {
- _tasks[i].hTask = 0;
- _tasks[i].argv = 0;
- _tasks[i].envp = 0;
- _tasks[i].pid = 0;
- _tasks[i].nChildren = 0;
- _tasks[i].nZombies = 0;
- _tasks[i].iFledgeling = -1;
- for (j = 0; j < UFILE_MAX; j++)
- _tasks[i].files[j] = -1;
- }
- htaskNow = GetCurrentTask();
- te.dwSize = sizeof(TASKENTRY);
- TaskFirst(&te);
- memset(_process, 0, sizeof(_process));
- memset(_zombies, 0, sizeof(_zombies));
- do
- {
- if (htaskNow == te.hTask)
- pidCurrent = nPIDNext;
- _process[nProcesses].pid = nPIDNext++;
- _process[nProcesses].pidParent = 1;
- _process[nProcesses].hTaskParent = 0;
- _process[nProcesses].hTask = te.hTask;
- _process[nProcesses].iParent = -1;
- nProcesses++;
- } while (TaskNext(&te));
- }
-
-
- int far _export
- tkern_kill( int pid,
- int nSignal)
- {
- int i;
- struct task *pt;
-
- pt = GetTaskInfo();
-
- for (i = 0; i < nProcesses; i++)
- {
- if (_process[i].pid == pid)
- {
- switch(nSignal)
- {
- case 0:
- break;
- case 1:
- case 2:
- case 9:
- case 14:
- TerminateApp(_process[i].hTask, NO_UAE_BOX);
- break;
-
- default:
- TerminateApp(_process[i].hTask, UAE_BOX);
- break;
- }
- return 0;
- }
- }
- for (i = 0; i < nZombies; i++)
- {
- if (_process[i].pid == pid)
- return 0; /* Can't kill the undead */
- }
- pt->nError = EFAULT;
- return -1;
- }
-
-
-
-
- void far _export
- tkern_register_sighandler(void far pascal (*_proc)(int))
- {
- struct task *pt;
-
- pt = GetTaskInfo();
- pt->intrproc = _proc;
- }
-
- void far _export
- tkern_deliver_signal( int nDevice,
- int nFile,
- int nSignal)
- {
- /* Find any task which is attached to the file and device,
- * and set its nSignal to the value supplied.
- */
-
- int idFile;
- int iTask;
- int iPTF;
-
- for (idFile = 0; idFile < TNFILE; idFile++)
- {
- if (_files[idFile].tf_dev == nDevice &&
- _files[idFile].tf_id == nFile)
- {
- break;
- }
- }
- if (idFile == TNFILE)
- return; /* Shouldn't be possible */
- for (iTask = 0; iTask < TNTASK; iTask++)
- {
- for (iPTF = 0; iPTF < UFILE_MAX; iPTF++)
- {
- if (_tasks[iTask].files[iPTF] == idFile)
- {
- if (!_tasks[iTask].intrproc)
- continue;
- _tasks[iTask].nSignal = nSignal;
- if (!(_tasks[iTask].nFlags & TF_ASLEEP))
- continue;
- PostAppMessage(_tasks[iTask].hTask, TKWM_WAKEUP, 0, 0);
- }
- }
- }
- }