Added kernel module for starting the usb stack at boot time. It's not activated thoug...
[cake.git] / rom / dos / createnewproc.c
blobb1337478547092d7f868cad25b838ea2d7134911
1 /*
2 Copyright © 1995-2009, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Create a new process
6 Lang: English
7 */
8 #include <exec/memory.h>
9 #include <exec/lists.h>
10 #include <proto/exec.h>
11 #include <dos/dosextens.h>
12 #include <dos/filesystem.h>
13 #include <dos/dostags.h>
14 #include <dos/stdio.h>
15 #define __DOS_NOLIBBASE__
16 #include <proto/dos.h>
17 #include <utility/tagitem.h>
18 #include <intuition/intuition.h>
19 #include <aros/symbolsets.h>
20 #include <proto/utility.h>
21 #include <proto/intuition.h>
22 #include <proto/graphics.h>
23 #include "dos_intern.h"
24 #include LC_LIBDEFS_FILE
25 #include <string.h>
27 static void KillCurrentProcess(void);
28 struct Process *AddProcess(struct Process *process, STRPTR argPtr,
29 ULONG argSize, APTR initialPC, APTR finalPC, struct DosLibrary *DOSBase);
31 static void freeLocalVars(struct Process *process, struct DosLibrary *DOSBase);
33 BOOL copyVars(struct Process *fromProcess, struct Process *toProcess, struct DosLibrary * DOSBase);
35 void internal_ChildWait(struct Task *task, struct DosLibrary * DOSBase);
36 void internal_ChildFree(APTR tid, struct DosLibrary * DOSBase);
38 static VOID TrapHandler(ULONG alertNum);
39 static LONG AskSuspend(const TEXT *taskName, ULONG alertNum);
41 #include <aros/debug.h>
43 /* Temporary macro */
44 #define P(x)
45 /*****************************************************************************
47 NAME */
48 #include <proto/dos.h>
50 AROS_LH1(struct Process *, CreateNewProc,
52 /* SYNOPSIS */
53 AROS_LHA(struct TagItem *, tags, D1),
55 /* LOCATION */
56 struct DosLibrary *, DOSBase, 83, Dos)
58 /* FUNCTION
59 Create a new process using the tagitem array.
61 INPUTS
62 tags - information on the new process.
64 RESULT
65 Pointer to the new process or NULL on error.
67 NOTES
69 EXAMPLE
71 BUGS
73 SEE ALSO
75 INTERNALS
77 *****************************************************************************/
79 AROS_LIBFUNC_INIT
81 /* Allocated resources */
82 struct Process *process = NULL;
83 BPTR input = 0, output = 0, ces = 0, curdir = 0, homedir = 0;
84 STRPTR stack = NULL, name = NULL, argptr = NULL;
85 ULONG namesize = 0, argsize = 0;
86 struct MemList *memlist = NULL;
87 struct CommandLineInterface *cli = NULL;
88 struct Process *me = (struct Process *)FindTask(NULL);
89 STRPTR s;
90 ULONG old_sig;
92 /* TODO: NP_CommandName, NP_ConsoleTask, NP_NotifyOnDeath */
94 #define TAGDATA_NOT_SPECIFIED ~0ul
96 struct TagItem defaults[]=
98 /* 0 */ { NP_Seglist , 0 },
99 /* 1 */ { NP_Entry , (IPTR)NULL },
100 /* 2 */ { NP_Input , TAGDATA_NOT_SPECIFIED },
101 /* 3 */ { NP_CloseInput , 1 },
102 /* 4 */ { NP_Output , TAGDATA_NOT_SPECIFIED },
103 /* 5 */ { NP_CloseOutput , 1 },
104 /* 6 */ { NP_Error , TAGDATA_NOT_SPECIFIED },
105 /* 7 */ { NP_CloseError , 1 },
106 /* 8 */ { NP_CurrentDir , TAGDATA_NOT_SPECIFIED },
107 /* 9 */ { NP_StackSize , AROS_STACKSIZE },
108 /*10 */ { NP_Name , (IPTR)"New Process" },
109 /*11 */ { NP_Priority , me->pr_Task.tc_Node.ln_Pri },
110 /*12 */ { NP_Arguments , (IPTR)NULL },
111 /*13 */ { NP_Cli , 0 },
112 /*14 */ { NP_UserData , (IPTR)NULL },
113 /*15 */ { NP_ExitCode , (IPTR)NULL },
114 /*16 */ { NP_ExitData , (IPTR)NULL },
115 /*17 */ { NP_WindowPtr , (IPTR)NULL }, /* Default: default public screen */
116 /*18 */ { NP_CopyVars , (IPTR)TRUE },
117 /*19 */ { NP_Synchronous , (IPTR)FALSE },
118 /*20 */ { NP_FreeSeglist , (IPTR)TRUE },
119 /*21 */ { NP_HomeDir , TAGDATA_NOT_SPECIFIED },
120 /*22 */ { NP_Path , TAGDATA_NOT_SPECIFIED }, /* Default: copy path from parent */
121 /*23 */ { NP_NotifyOnDeath , (IPTR)FALSE },
122 { TAG_END , 0 }
125 /* C has no exceptions. This is a simple replacement. */
126 #define ERROR_IF(a) if(a) goto error /* Throw a generic error. */
127 #define ENOMEM_IF(a) if (a) goto enomem /* Throw out of memory. */
128 /* Inherit the parent process' stacksize if possible */
129 if (__is_process(me))
131 struct CommandLineInterface *cli = Cli();
133 if (cli)
135 LONG parentstack = cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT;
137 if (parentstack > AROS_STACKSIZE)
139 defaults[9].ti_Data = parentstack;
144 ApplyTagChanges(defaults, tags);
146 /* If both the seglist and the entry are specified, make sure that the entry resides in the seglist */
147 if (defaults[0].ti_Data && defaults[1].ti_Data)
149 BPTR seg;
151 for (seg = (BPTR) defaults[0].ti_Data; seg; seg = *(BPTR *)BADDR(seg))
155 (UBYTE *)defaults[1].ti_Data >= (UBYTE *)BADDR(seg) &&
156 (UBYTE *)defaults[1].ti_Data <= ((UBYTE *)BADDR(seg) + *((ULONG *)BADDR(seg) - 1) - sizeof(BPTR))
159 break;
163 if (!seg)
164 return NULL;
167 process = (struct Process *)AllocMem(sizeof(struct Process),
168 MEMF_PUBLIC | MEMF_CLEAR);
169 ENOMEM_IF(process == NULL);
171 /* Do this early to ease implementation of failure code */
172 NEWLIST((struct List *)&process->pr_LocalVars);
174 /* We need a minimum stack to handle interrupt contexts */
175 if (defaults[9].ti_Data < AROS_STACKSIZE)
177 defaults[9].ti_Data = AROS_STACKSIZE;
180 stack = AllocMem(defaults[9].ti_Data, MEMF_PUBLIC);
181 ENOMEM_IF(stack == NULL);
183 s = (STRPTR)defaults[10].ti_Data;
185 while (*s++);
187 namesize = s - (STRPTR)defaults[10].ti_Data;
189 name = AllocMem(namesize, MEMF_PUBLIC);
190 ENOMEM_IF(name == NULL);
192 /* NP_Arguments */
193 s = (STRPTR)defaults[12].ti_Data;
195 if (s != NULL)
197 while(*s++);
199 argsize = s - (STRPTR)defaults[12].ti_Data;
200 argptr = (STRPTR)AllocVec(argsize, MEMF_PUBLIC);
201 ENOMEM_IF(argptr == NULL);
204 memlist = AllocMem(sizeof(struct MemList) + 2*sizeof(struct MemEntry),
205 MEMF_ANY);
206 ENOMEM_IF(memlist == NULL);
208 /* NP_Cli */
209 if (defaults[13].ti_Data != 0)
211 BPTR *oldpath = NULL;
213 /* Don't forget to pass tags to AllocDosObject() */
214 cli = (struct CommandLineInterface *)AllocDosObject(DOS_CLI, tags);
215 ENOMEM_IF(cli == NULL);
217 cli->cli_DefaultStack = (defaults[9].ti_Data + CLI_DEFAULTSTACK_UNIT - 1) / CLI_DEFAULTSTACK_UNIT;
219 if (__is_process(me))
221 struct CommandLineInterface *oldcli = Cli();
223 if (oldcli != NULL)
225 LONG oldlen = AROS_BSTR_strlen(oldcli->cli_Prompt);
226 LONG newlen = GetTagData(ADO_PromptLen, 255, tags);
228 oldpath = BADDR(oldcli->cli_CommandDir);
230 CopyMem(BADDR(oldcli->cli_Prompt), BADDR(cli->cli_Prompt), (newlen<oldlen?newlen:oldlen) + 1);
235 if (defaults[22].ti_Data != TAGDATA_NOT_SPECIFIED)
237 cli->cli_CommandDir = (BPTR) defaults[22].ti_Data;
239 else
241 BPTR *nextpath,
242 *newpath = &cli->cli_CommandDir;
244 while (oldpath != NULL)
246 nextpath = AllocVec(2*sizeof(BPTR), MEMF_CLEAR);
247 ENOMEM_IF(nextpath == NULL);
249 newpath[0] = MKBADDR(nextpath);
250 nextpath[1] = DupLock(oldpath[1]);
251 ERROR_IF(!nextpath[1]);
253 newpath = nextpath;
254 oldpath = BADDR(oldpath[0]);
259 /* NP_Input */
261 if (defaults[2].ti_Data == TAGDATA_NOT_SPECIFIED)
263 if (__is_process(me))
265 input = Open("NIL:", MODE_OLDFILE);
266 ERROR_IF(!input);
268 defaults[2].ti_Data = (IPTR)input;
270 else
272 defaults[2].ti_Data = 0;
276 /* NP_Output */
278 if (defaults[4].ti_Data == TAGDATA_NOT_SPECIFIED)
280 if (__is_process(me))
282 output = Open("NIL:", MODE_NEWFILE);
283 ERROR_IF(!output);
285 defaults[4].ti_Data = (IPTR)output;
287 else
289 defaults[4].ti_Data = 0;
293 /* NP_Error */
295 if (defaults[6].ti_Data == TAGDATA_NOT_SPECIFIED)
297 if (__is_process(me))
299 ces = Open("NIL:", MODE_NEWFILE);
300 ERROR_IF(!ces);
302 defaults[6].ti_Data = (IPTR)ces;
304 else
306 defaults[6].ti_Data = 0;
310 if (defaults[6].ti_Data)
312 /* unbuffered to conform to widespread clib behavior */
313 SetVBuf((BPTR) defaults[6].ti_Data, NULL, BUF_NONE, -1);
316 /* NP_CurrentDir */
318 if (defaults[8].ti_Data == TAGDATA_NOT_SPECIFIED)
320 if (__is_process(me) && me->pr_CurrentDir)
322 curdir = Lock("", SHARED_LOCK);
323 ERROR_IF(!curdir);
325 defaults[8].ti_Data = (IPTR)curdir;
327 else
329 defaults[8].ti_Data = 0;
333 /* NP_HomeDir */
335 if (defaults[21].ti_Data == TAGDATA_NOT_SPECIFIED)
337 defaults[21].ti_Data = 0;
339 if (__is_process(me))
341 if (me->pr_HomeDir)
343 homedir = DupLock(me->pr_HomeDir);
344 ERROR_IF(!homedir);
346 defaults[21].ti_Data = (IPTR)homedir;
351 CopyMem((APTR)defaults[10].ti_Data, name, namesize);
352 CopyMem((APTR)defaults[12].ti_Data, argptr, argsize);
354 process->pr_Task.tc_Node.ln_Type = NT_PROCESS;
355 process->pr_Task.tc_Node.ln_Name = name;
356 process->pr_Task.tc_Node.ln_Pri = defaults[11].ti_Data;
357 process->pr_Task.tc_TrapCode = TrapHandler;
358 process->pr_Task.tc_SPLower = stack;
359 process->pr_Task.tc_SPUpper = stack + defaults[9].ti_Data;
361 /* process->pr_ReturnAddr; */
362 NEWLIST(&process->pr_Task.tc_MemEntry);
364 memlist->ml_NumEntries = 3;
365 memlist->ml_ME[0].me_Addr = process;
366 memlist->ml_ME[0].me_Length = sizeof(struct Process);
367 memlist->ml_ME[1].me_Addr = stack;
368 memlist->ml_ME[1].me_Length = defaults[9].ti_Data;
369 memlist->ml_ME[2].me_Addr = name;
370 memlist->ml_ME[2].me_Length = namesize;
372 AddHead(&process->pr_Task.tc_MemEntry, &memlist->ml_Node);
374 process->pr_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
375 process->pr_MsgPort.mp_Flags = PA_SIGNAL;
376 process->pr_MsgPort.mp_SigBit = SIGB_DOS;
377 process->pr_MsgPort.mp_SigTask = process;
379 NEWLIST(&process->pr_MsgPort.mp_MsgList);
381 process->pr_SegList = (BPTR)defaults[0].ti_Data;
382 process->pr_StackSize = defaults[9].ti_Data;
383 process->pr_GlobVec = NULL; /* Unused BCPL crap */
384 process->pr_StackBase = MKBADDR(process->pr_Task.tc_SPUpper);
385 process->pr_Result2 = 0;
386 process->pr_CurrentDir = (BPTR)defaults[8].ti_Data;
387 process->pr_CIS = (BPTR)defaults[2].ti_Data;
388 process->pr_COS = (BPTR)defaults[4].ti_Data;
389 process->pr_CES = (BPTR)defaults[6].ti_Data;
390 process->pr_Task.tc_UserData = (APTR)defaults[14].ti_Data;
392 /* process->pr_ConsoleTask=; */
393 /* process->pr_FileSystemTask=; */
394 process->pr_CLI = MKBADDR(cli);
396 /* Set the name of this program */
397 internal_SetProgramName(cli, name, DOSBase);
398 D(bug("Calling internal_SetProgramName() with name = %s\n", name));
400 process->pr_PktWait = NULL;
401 process->pr_WindowPtr = (struct Window *)defaults[17].ti_Data;
402 process->pr_HomeDir = (BPTR)defaults[21].ti_Data;
403 process->pr_Flags = (defaults[3].ti_Data ? PRF_CLOSEINPUT : 0) |
404 (defaults[5].ti_Data ? PRF_CLOSEOUTPUT : 0) |
405 (defaults[7].ti_Data ? PRF_CLOSEERROR : 0) |
406 (defaults[13].ti_Data ? PRF_FREECLI : 0) |
407 (defaults[19].ti_Data ? PRF_SYNCHRONOUS : 0) |
408 (defaults[20].ti_Data ? PRF_FREESEGLIST : 0) |
409 (defaults[23].ti_Data ? PRF_NOTIFYONDEATH : 0) |
410 PRF_FREEARGS | PRF_FREECURRDIR;
411 process->pr_ExitCode = (APTR)defaults[15].ti_Data;
412 process->pr_ExitData = defaults[16].ti_Data;
413 process->pr_Arguments = argptr;
415 if ((BOOL)defaults[18].ti_Data) /* NP_CopyVars */
417 BOOL res = copyVars(me, process, DOSBase);
419 ENOMEM_IF(res == FALSE);
422 process->pr_ShellPrivate = 0;
425 if (defaults[19].ti_Data)
427 me->pr_Flags |= PRF_WAITINGFORCHILD;
429 old_sig = SetSignal(0L, SIGF_SINGLE) & SIGF_SINGLE;
432 /* argsize variable includes trailing 0 byte, but is supposed
433 not to. */
435 if (argsize) argsize--;
440 AddProcess
442 process, argptr, argsize,
443 defaults[1].ti_Data ?
444 (APTR)defaults[1].ti_Data:
445 (APTR)((BPTR *)BADDR(defaults[0].ti_Data) + 1),
446 KillCurrentProcess, DOSBase
450 /* NP_Synchronous */
451 if (defaults[19].ti_Data)
453 P(kprintf("Calling ChildWait()\n"));
454 internal_ChildWait(FindTask(NULL), DOSBase);
455 P(kprintf("Returned from ChildWait()\n"));
458 goto end;
461 /* Fall through */
462 enomem:
463 if (__is_process(me))
465 SetIoErr(ERROR_NO_FREE_STORE);
468 freeLocalVars(process, DOSBase);
470 error:
471 if (cli != NULL)
473 FreeDosObject(DOS_CLI, cli);
476 if (homedir != NULL)
478 UnLock(homedir);
481 if (curdir != NULL)
483 UnLock(curdir);
486 if (output != NULL)
488 Close(output);
491 if (input != NULL)
493 Close(input);
496 if (ces != NULL)
498 Close(ces);
501 if (argptr != NULL)
503 FreeVec(argptr);
506 if (memlist != NULL)
508 FreeMem(memlist, sizeof(struct MemList) + 2*sizeof(struct MemEntry));
511 if (name != NULL)
513 FreeMem(name, namesize);
516 if (stack != NULL)
518 FreeMem(stack, defaults[9].ti_Data);
521 if (process != NULL)
523 FreeMem(process, sizeof(struct Process));
525 process = NULL;
528 end:
530 if (defaults[19].ti_Data)
531 SetSignal(SIGF_SINGLE, old_sig);
533 return process;
535 AROS_LIBFUNC_EXIT
536 } /* CreateNewProc */
542 static void freeLocalVars(struct Process *process, struct DosLibrary *DOSBase)
544 struct LocalVar *varNode;
545 struct Node *tempNode;
547 ForeachNodeSafe(&process->pr_LocalVars,
548 varNode, tempNode)
550 P(kprintf("Freeing variable %s with value %s at %p\n",
551 varNode->lv_Node.ln_Name, varNode->lv_Value, varNode));
552 FreeMem(varNode->lv_Value, varNode->lv_Len);
553 Remove((struct Node *)varNode);
554 FreeVec(varNode);
559 void internal_ChildWait(struct Task *task, struct DosLibrary * DOSBase)
561 while (((struct Process *)task)->pr_Flags & PRF_WAITINGFORCHILD)
562 Wait(SIGF_SINGLE);
566 void internal_ChildFree(APTR tid, struct DosLibrary * DOSBase)
568 struct Task *task = (struct Task *)tid;
569 struct Process *parent = (struct Process *)(GetETask(task)->et_Parent);
571 D(bug("Awakening the parent task %p (called %s)\n", parent, parent->pr_Task.tc_Node.ln_Name));
573 parent->pr_Flags &= ~PRF_WAITINGFORCHILD;
574 Signal(&parent->pr_Task, SIGF_SINGLE);
578 BOOL copyVars(struct Process *fromProcess, struct Process *toProcess, struct DosLibrary * DOSBase)
580 /* We must have variables to copy... */
581 if (__is_process(fromProcess))
583 struct LocalVar *varNode;
584 struct LocalVar *newVar;
586 /* We use the same strategy as in the ***Var() functions */
587 ForeachNode(&fromProcess->pr_LocalVars, varNode)
589 LONG copyLength = strlen(varNode->lv_Node.ln_Name) + 1 +
590 sizeof(struct LocalVar);
592 newVar = (struct LocalVar *)AllocVec(copyLength,
593 MEMF_PUBLIC | MEMF_CLEAR);
594 if (newVar == NULL)
595 return FALSE;
597 CopyMem(varNode, newVar, copyLength);
598 newVar->lv_Node.ln_Name = (char *)newVar +
599 sizeof(struct LocalVar);
600 P(kprintf("Variable with name %s copied.\n",
601 newVar->lv_Node.ln_Name));
603 if (varNode->lv_Len)
605 newVar->lv_Value = AllocMem(varNode->lv_Len, MEMF_PUBLIC);
607 if (newVar->lv_Value == NULL)
609 /* Free variable node before shutting down */
610 FreeVec(newVar);
612 return FALSE;
615 CopyMem(varNode->lv_Value, newVar->lv_Value, varNode->lv_Len);
618 AddTail((struct List *)&toProcess->pr_LocalVars,
619 (struct Node *)newVar);
623 return TRUE;
626 #warning Q: Is there a better way to pass DOSBase to KillCurrentProcess ?
627 static struct DosLibrary *DOSBase;
629 static int SetDosBase(LIBBASETYPEPTR __DOSBase)
631 D(bug("SetDosBase\n"));
632 DOSBase = __DOSBase;
633 return TRUE;
636 /* At pri -1 so it is executed before the DosInit in dos_init.c */
637 ADD2INITLIB(SetDosBase, -1)
639 static void KillCurrentProcess(void)
641 struct Process *me;
643 me = (struct Process *)FindTask(NULL);
645 /* Call user defined exit function before shutting down. */
646 if (me->pr_ExitCode != NULL)
649 The Ralph Bebel's guru book says that pr_ExitCode
650 is passed the process return code in D0 and pr_ExitData in D1,
651 but the Matt Dillon's DICE C implementation of vfork shows that
652 those parameters are passed also on the stack.
654 The AROS macros for functions with register parameters don't
655 support both register and stack parameters at once, so we use
656 the stack only. This oughta be fixed somehow.
658 me->pr_ExitCode(me->pr_Task.tc_UserData, me->pr_ExitData);
661 P(kprintf("Deleting local variables\n"));
663 /* Clean up */
664 freeLocalVars(me, DOSBase);
666 P(kprintf("Closing input stream\n"));
668 if (me->pr_Flags & PRF_CLOSEINPUT)
670 Close(me->pr_CIS);
673 P(kprintf("Closing output stream\n"));
675 if (me->pr_Flags & PRF_CLOSEOUTPUT)
677 Close(me->pr_COS);
680 P(kprintf("Closing error stream\n"));
682 if (me->pr_Flags & PRF_CLOSEERROR)
684 Close(me->pr_CES);
687 P(kprintf("Freeing arguments\n"));
689 if (me->pr_Flags & PRF_FREEARGS)
691 FreeVec(me->pr_Arguments);
694 P(kprintf("Unloading segment\n"));
696 if (me->pr_Flags & PRF_FREESEGLIST)
698 UnLoadSeg(me->pr_SegList);
701 P(kprintf("Unlocking current dir\n"));
703 if (me->pr_Flags & PRF_FREECURRDIR)
705 UnLock(me->pr_CurrentDir);
708 P(kprintf("Unlocking home dir\n"));
709 UnLock(me->pr_HomeDir);
711 P(kprintf("Freeing cli structure\n"));
713 if (me->pr_Flags & PRF_FREECLI)
715 FreeDosObject(DOS_CLI, BADDR(me->pr_CLI));
718 /* To implement NP_Synchronous and NP_NotifyOnDeath I need Child***()
719 here */
721 // if(me->pr_Flags & PRF_NOTIFYONDEATH)
722 // Signal(GetETask(me)->iet_Parent, SIGF_CHILD);
724 if (me->pr_Flags & PRF_SYNCHRONOUS)
726 P(kprintf("Calling ChildFree()\n"));
728 // ChildStatus(me);
729 internal_ChildFree(me, DOSBase);
732 removefromrootnode(me, DOSBase);
734 RemTask(NULL);
736 CloseLibrary((struct Library * )DOSBase);
739 static VOID TrapHandler(ULONG alertNum)
741 struct Task *task = FindTask(NULL);
743 if (AskSuspend(task->tc_Node.ln_Name, alertNum) == 1)
745 Wait(0);
747 else
749 ShowImminentReset();
750 ShutdownA(SD_ACTION_COLDREBOOT);
754 static LONG AskSuspend(const TEXT *taskName, ULONG alertNum)
756 struct EasyStruct es =
758 sizeof (struct EasyStruct),
760 "Software Failure",
761 "%s\nProgram failed (error #%08lx).\n"
762 "Wait for disk activity to finish.",
763 "Suspend|Reboot"
765 CONST_APTR args[] = {taskName, (CONST_APTR)alertNum, NULL};
766 LONG choice = 0;
768 es.es_TextFormat = "%s\nProgram failed (error #%08lx).\n"
769 "Wait for disk activity to finish.";
770 if (IntuitionBase != NULL)
772 if (IntuitionBase->FirstScreen != NULL)
774 choice = EasyRequestArgs(NULL, &es, NULL, args);
778 return choice;