added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / rom / dos / createnewproc.c
blobef3307df6533f21a07aaba9dcf3bace29efeacf1
1 /*
2 Copyright © 1995-2007, 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 <aros/symbolsets.h>
19 #include <proto/utility.h>
20 #include "dos_intern.h"
21 #include LC_LIBDEFS_FILE
22 #include <string.h>
24 static void KillCurrentProcess(void);
25 struct Process *AddProcess(struct Process *process, STRPTR argPtr,
26 ULONG argSize, APTR initialPC, APTR finalPC, struct DosLibrary *DOSBase);
28 static void freeLocalVars(struct Process *process, struct DosLibrary *DOSBase);
30 BOOL copyVars(struct Process *fromProcess, struct Process *toProcess, struct DosLibrary * DOSBase);
32 void internal_ChildWait(struct Task *task, struct DosLibrary * DOSBase);
33 void internal_ChildFree(APTR tid, struct DosLibrary * DOSBase);
35 #include <aros/debug.h>
37 /* Temporary macro */
38 #define P(x)
39 /*****************************************************************************
41 NAME */
42 #include <proto/dos.h>
44 AROS_LH1(struct Process *, CreateNewProc,
46 /* SYNOPSIS */
47 AROS_LHA(struct TagItem *, tags, D1),
49 /* LOCATION */
50 struct DosLibrary *, DOSBase, 83, Dos)
52 /* FUNCTION
53 Create a new process using the tagitem array.
55 INPUTS
56 tags - information on the new process.
58 RESULT
59 Pointer to the new process or NULL on error.
61 NOTES
63 EXAMPLE
65 BUGS
67 SEE ALSO
69 INTERNALS
71 *****************************************************************************/
73 AROS_LIBFUNC_INIT
75 /* Allocated resources */
76 struct Process *process = NULL;
77 BPTR input = 0, output = 0, ces = 0, curdir = 0, homedir = 0;
78 STRPTR stack = NULL, name = NULL, argptr = NULL;
79 ULONG namesize = 0, argsize = 0;
80 struct MemList *memlist = NULL;
81 struct CommandLineInterface *cli = NULL;
82 struct Process *me = (struct Process *)FindTask(NULL);
83 STRPTR s;
84 ULONG old_sig;
86 /* TODO: NP_CommandName, NP_ConsoleTask, NP_NotifyOnDeath */
88 #define TAGDATA_NOT_SPECIFIED ~0ul
90 struct TagItem defaults[]=
92 /* 0 */ { NP_Seglist , 0 },
93 /* 1 */ { NP_Entry , (IPTR)NULL },
94 /* 2 */ { NP_Input , TAGDATA_NOT_SPECIFIED },
95 /* 3 */ { NP_CloseInput , 1 },
96 /* 4 */ { NP_Output , TAGDATA_NOT_SPECIFIED },
97 /* 5 */ { NP_CloseOutput , 1 },
98 /* 6 */ { NP_Error , TAGDATA_NOT_SPECIFIED },
99 /* 7 */ { NP_CloseError , 1 },
100 /* 8 */ { NP_CurrentDir , TAGDATA_NOT_SPECIFIED },
101 /* 9 */ { NP_StackSize , AROS_STACKSIZE },
102 /*10 */ { NP_Name , (IPTR)"New Process" },
103 /*11 */ { NP_Priority , me->pr_Task.tc_Node.ln_Pri },
104 /*12 */ { NP_Arguments , (IPTR)NULL },
105 /*13 */ { NP_Cli , 0 },
106 /*14 */ { NP_UserData , (IPTR)NULL },
107 /*15 */ { NP_ExitCode , (IPTR)NULL },
108 /*16 */ { NP_ExitData , (IPTR)NULL },
109 /*17 */ { NP_WindowPtr , (IPTR)NULL }, /* Default: default public screen */
110 /*18 */ { NP_CopyVars , (IPTR)TRUE },
111 /*19 */ { NP_Synchronous , (IPTR)FALSE },
112 /*20 */ { NP_FreeSeglist , (IPTR)TRUE },
113 /*21 */ { NP_HomeDir , TAGDATA_NOT_SPECIFIED },
114 /*22 */ { NP_Path , TAGDATA_NOT_SPECIFIED }, /* Default: copy path from parent */
115 { TAG_END , 0 }
118 /* C has no exceptions. This is a simple replacement. */
119 #define ERROR_IF(a) if(a) goto error /* Throw a generic error. */
120 #define ENOMEM_IF(a) if (a) goto enomem /* Throw out of memory. */
121 /* Inherit the parent process' stacksize if possible */
122 if (__is_process(me))
124 struct CommandLineInterface *cli = Cli();
126 if (cli)
128 LONG parentstack = cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT;
130 if (parentstack > AROS_STACKSIZE)
132 defaults[9].ti_Data = parentstack;
137 ApplyTagChanges(defaults, tags);
139 /* If both the seglist and the entry are specified, make sure that the entry resides in the seglist */
140 if (defaults[0].ti_Data && defaults[1].ti_Data)
142 BPTR seg;
144 for (seg = (BPTR) defaults[0].ti_Data; seg; seg = *(BPTR *)BADDR(seg))
148 (UBYTE *)defaults[1].ti_Data >= (UBYTE *)BADDR(seg) &&
149 (UBYTE *)defaults[1].ti_Data <= ((UBYTE *)BADDR(seg) + *((ULONG *)BADDR(seg) - 1) - sizeof(BPTR))
152 break;
156 if (!seg)
157 return NULL;
160 process = (struct Process *)AllocMem(sizeof(struct Process),
161 MEMF_PUBLIC | MEMF_CLEAR);
162 ENOMEM_IF(process == NULL);
164 /* Do this early to ease implementation of failure code */
165 NEWLIST((struct List *)&process->pr_LocalVars);
167 /* We need a minimum stack to handle interrupt contexts */
168 if (defaults[9].ti_Data < AROS_STACKSIZE)
170 defaults[9].ti_Data = AROS_STACKSIZE;
173 stack = AllocMem(defaults[9].ti_Data, MEMF_PUBLIC);
174 ENOMEM_IF(stack == NULL);
176 s = (STRPTR)defaults[10].ti_Data;
178 while (*s++);
180 namesize = s - (STRPTR)defaults[10].ti_Data;
182 name = AllocMem(namesize, MEMF_PUBLIC);
183 ENOMEM_IF(name == NULL);
185 /* NP_Arguments */
186 s = (STRPTR)defaults[12].ti_Data;
188 if (s != NULL)
190 while(*s++);
192 argsize = s - (STRPTR)defaults[12].ti_Data;
193 argptr = (STRPTR)AllocVec(argsize, MEMF_PUBLIC);
194 ENOMEM_IF(argptr == NULL);
197 memlist = AllocMem(sizeof(struct MemList) + 2*sizeof(struct MemEntry),
198 MEMF_ANY);
199 ENOMEM_IF(memlist == NULL);
201 /* NP_Cli */
202 if (defaults[13].ti_Data != 0)
204 BPTR *oldpath = NULL;
206 /* Don't forget to pass tags to AllocDosObject() */
207 cli = (struct CommandLineInterface *)AllocDosObject(DOS_CLI, tags);
208 ENOMEM_IF(cli == NULL);
210 cli->cli_DefaultStack = (defaults[9].ti_Data + CLI_DEFAULTSTACK_UNIT - 1) / CLI_DEFAULTSTACK_UNIT;
212 if (__is_process(me))
214 struct CommandLineInterface *oldcli = Cli();
216 if (oldcli != NULL)
218 LONG oldlen = AROS_BSTR_strlen(oldcli->cli_Prompt);
219 LONG newlen = GetTagData(ADO_PromptLen, 255, tags);
221 oldpath = BADDR(oldcli->cli_CommandDir);
223 CopyMem(BADDR(oldcli->cli_Prompt), BADDR(cli->cli_Prompt), (newlen<oldlen?newlen:oldlen) + 1);
228 if (defaults[22].ti_Data != TAGDATA_NOT_SPECIFIED)
230 cli->cli_CommandDir = (BPTR) defaults[22].ti_Data;
232 else
234 BPTR *nextpath,
235 *newpath = &cli->cli_CommandDir;
237 while (oldpath != NULL)
239 nextpath = AllocVec(2*sizeof(BPTR), MEMF_CLEAR);
240 ENOMEM_IF(nextpath == NULL);
242 newpath[0] = MKBADDR(nextpath);
243 nextpath[1] = DupLock(oldpath[1]);
244 ERROR_IF(!nextpath[1]);
246 newpath = nextpath;
247 oldpath = BADDR(oldpath[0]);
252 /* NP_Input */
254 if (defaults[2].ti_Data == TAGDATA_NOT_SPECIFIED)
256 if (__is_process(me))
258 input = Open("NIL:", MODE_OLDFILE);
259 ERROR_IF(!input);
261 defaults[2].ti_Data = (IPTR)input;
263 else
265 defaults[2].ti_Data = 0;
269 /* NP_Output */
271 if (defaults[4].ti_Data == TAGDATA_NOT_SPECIFIED)
273 if (__is_process(me))
275 output = Open("NIL:", MODE_NEWFILE);
276 ERROR_IF(!output);
278 defaults[4].ti_Data = (IPTR)output;
280 else
282 defaults[4].ti_Data = 0;
286 /* NP_Error */
288 if (defaults[6].ti_Data == TAGDATA_NOT_SPECIFIED)
290 if (__is_process(me))
292 ces = Open("NIL:", MODE_NEWFILE);
293 ERROR_IF(!ces);
295 defaults[6].ti_Data = (IPTR)ces;
297 else
299 defaults[6].ti_Data = 0;
303 if (defaults[6].ti_Data)
305 /* unbuffered to conform to widespread clib behavior */
306 SetVBuf((BPTR) defaults[6].ti_Data, NULL, BUF_NONE, -1);
309 /* NP_CurrentDir */
311 if (defaults[8].ti_Data == TAGDATA_NOT_SPECIFIED)
313 if (__is_process(me) && me->pr_CurrentDir)
315 curdir = Lock("", SHARED_LOCK);
316 ERROR_IF(!curdir);
318 defaults[8].ti_Data = (IPTR)curdir;
320 else
322 defaults[8].ti_Data = 0;
326 /* NP_HomeDir */
328 if (defaults[21].ti_Data == TAGDATA_NOT_SPECIFIED)
330 defaults[21].ti_Data = 0;
332 if (__is_process(me))
334 if (me->pr_HomeDir)
336 homedir = DupLock(me->pr_HomeDir);
337 ERROR_IF(!homedir);
339 defaults[21].ti_Data = (IPTR)homedir;
344 CopyMem((APTR)defaults[10].ti_Data, name, namesize);
345 CopyMem((APTR)defaults[12].ti_Data, argptr, argsize);
347 process->pr_Task.tc_Node.ln_Type = NT_PROCESS;
348 process->pr_Task.tc_Node.ln_Name = name;
349 process->pr_Task.tc_Node.ln_Pri = defaults[11].ti_Data;
350 process->pr_Task.tc_SPLower = stack;
351 process->pr_Task.tc_SPUpper = stack + defaults[9].ti_Data;
353 /* process->pr_ReturnAddr; */
354 NEWLIST(&process->pr_Task.tc_MemEntry);
356 memlist->ml_NumEntries = 3;
357 memlist->ml_ME[0].me_Addr = process;
358 memlist->ml_ME[0].me_Length = sizeof(struct Process);
359 memlist->ml_ME[1].me_Addr = stack;
360 memlist->ml_ME[1].me_Length = defaults[9].ti_Data;
361 memlist->ml_ME[2].me_Addr = name;
362 memlist->ml_ME[2].me_Length = namesize;
364 AddHead(&process->pr_Task.tc_MemEntry, &memlist->ml_Node);
366 process->pr_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
367 process->pr_MsgPort.mp_Flags = PA_SIGNAL;
368 process->pr_MsgPort.mp_SigBit = SIGB_DOS;
369 process->pr_MsgPort.mp_SigTask = process;
371 NEWLIST(&process->pr_MsgPort.mp_MsgList);
373 process->pr_SegList = (BPTR)defaults[0].ti_Data;
374 process->pr_StackSize = defaults[9].ti_Data;
375 process->pr_GlobVec = NULL; /* Unused BCPL crap */
376 process->pr_StackBase = MKBADDR(process->pr_Task.tc_SPUpper);
377 process->pr_Result2 = 0;
378 process->pr_CurrentDir = (BPTR)defaults[8].ti_Data;
379 process->pr_CIS = (BPTR)defaults[2].ti_Data;
380 process->pr_COS = (BPTR)defaults[4].ti_Data;
381 process->pr_CES = (BPTR)defaults[6].ti_Data;
382 process->pr_Task.tc_UserData = (APTR)defaults[14].ti_Data;
384 /* process->pr_ConsoleTask=; */
385 /* process->pr_FileSystemTask=; */
386 process->pr_CLI = MKBADDR(cli);
388 /* Set the name of this program */
389 internal_SetProgramName(cli, name, DOSBase);
390 D(bug("Calling internal_SetProgramName() with name = %s\n", name));
392 process->pr_PktWait = NULL;
393 process->pr_WindowPtr = (struct Window *)defaults[17].ti_Data;
394 process->pr_HomeDir = (BPTR)defaults[21].ti_Data;
395 process->pr_Flags = (defaults[3].ti_Data ? PRF_CLOSEINPUT : 0) |
396 (defaults[5].ti_Data ? PRF_CLOSEOUTPUT : 0) |
397 (defaults[7].ti_Data ? PRF_CLOSEERROR : 0) |
398 (defaults[13].ti_Data ? PRF_FREECLI : 0) |
399 (defaults[19].ti_Data ? PRF_SYNCHRONOUS : 0) |
400 (defaults[20].ti_Data ? PRF_FREESEGLIST : 0) |
401 PRF_FREEARGS | PRF_FREECURRDIR;
402 process->pr_ExitCode = (APTR)defaults[15].ti_Data;
403 process->pr_ExitData = defaults[16].ti_Data;
404 process->pr_Arguments = argptr;
406 if ((BOOL)defaults[18].ti_Data) /* NP_CopyVars */
408 BOOL res = copyVars(me, process, DOSBase);
410 ENOMEM_IF(res == FALSE);
413 process->pr_ShellPrivate = 0;
416 if (defaults[19].ti_Data)
418 me->pr_Flags |= PRF_WAITINGFORCHILD;
420 old_sig = SetSignal(0L, SIGF_SINGLE) & SIGF_SINGLE;
423 /* argsize variable includes trailing 0 byte, but is supposed
424 not to. */
426 if (argsize) argsize--;
431 AddProcess
433 process, argptr, argsize,
434 defaults[1].ti_Data ?
435 (APTR)defaults[1].ti_Data:
436 (APTR)((BPTR *)BADDR(defaults[0].ti_Data) + 1),
437 KillCurrentProcess, DOSBase
441 /* NP_Synchronous */
442 if (defaults[19].ti_Data)
444 P(kprintf("Calling ChildWait()\n"));
445 internal_ChildWait(FindTask(NULL), DOSBase);
446 P(kprintf("Returned from ChildWait()\n"));
449 goto end;
452 /* Fall through */
453 enomem:
454 if (__is_process(me))
456 SetIoErr(ERROR_NO_FREE_STORE);
459 freeLocalVars(process, DOSBase);
461 error:
462 if (cli != NULL)
464 FreeDosObject(DOS_CLI, cli);
467 if (homedir != NULL)
469 UnLock(homedir);
472 if (curdir != NULL)
474 UnLock(curdir);
477 if (output != NULL)
479 Close(output);
482 if (input != NULL)
484 Close(input);
487 if (ces != NULL)
489 Close(ces);
492 if (argptr != NULL)
494 FreeVec(argptr);
497 if (memlist != NULL)
499 FreeMem(memlist, sizeof(struct MemList) + 2*sizeof(struct MemEntry));
502 if (name != NULL)
504 FreeMem(name, namesize);
507 if (stack != NULL)
509 FreeMem(stack, defaults[9].ti_Data);
512 if (process != NULL)
514 FreeMem(process, sizeof(struct Process));
516 process = NULL;
519 end:
521 if (defaults[19].ti_Data)
522 SetSignal(SIGF_SINGLE, old_sig);
524 return process;
526 AROS_LIBFUNC_EXIT
527 } /* CreateNewProc */
533 static void freeLocalVars(struct Process *process, struct DosLibrary *DOSBase)
535 struct LocalVar *varNode;
536 struct Node *tempNode;
538 ForeachNodeSafe(&process->pr_LocalVars,
539 varNode, tempNode)
541 P(kprintf("Freeing variable %s with value %s at %p\n",
542 varNode->lv_Node.ln_Name, varNode->lv_Value, varNode));
543 FreeMem(varNode->lv_Value, varNode->lv_Len);
544 Remove((struct Node *)varNode);
545 FreeVec(varNode);
550 void internal_ChildWait(struct Task *task, struct DosLibrary * DOSBase)
552 while (((struct Process *)task)->pr_Flags & PRF_WAITINGFORCHILD)
553 Wait(SIGF_SINGLE);
557 void internal_ChildFree(APTR tid, struct DosLibrary * DOSBase)
559 struct Task *task = (struct Task *)tid;
560 struct Process *parent = (struct Process *)(GetETask(task)->et_Parent);
562 D(bug("Awakening the parent task %p (called %s)\n", parent, parent->pr_Task.tc_Node.ln_Name));
564 parent->pr_Flags &= ~PRF_WAITINGFORCHILD;
565 Signal(&parent->pr_Task, SIGF_SINGLE);
569 BOOL copyVars(struct Process *fromProcess, struct Process *toProcess, struct DosLibrary * DOSBase)
571 /* We must have variables to copy... */
572 if (__is_process(fromProcess))
574 struct LocalVar *varNode;
575 struct LocalVar *newVar;
577 /* We use the same strategy as in the ***Var() functions */
578 ForeachNode(&fromProcess->pr_LocalVars, varNode)
580 LONG copyLength = strlen(varNode->lv_Node.ln_Name) + 1 +
581 sizeof(struct LocalVar);
583 newVar = (struct LocalVar *)AllocVec(copyLength,
584 MEMF_PUBLIC | MEMF_CLEAR);
585 if (newVar == NULL)
586 return FALSE;
588 CopyMem(varNode, newVar, copyLength);
589 newVar->lv_Node.ln_Name = (char *)newVar +
590 sizeof(struct LocalVar);
591 P(kprintf("Variable with name %s copied.\n",
592 newVar->lv_Node.ln_Name));
594 if (varNode->lv_Len)
596 newVar->lv_Value = AllocMem(varNode->lv_Len, MEMF_PUBLIC);
598 if (newVar->lv_Value == NULL)
600 /* Free variable node before shutting down */
601 FreeVec(newVar);
603 return FALSE;
606 CopyMem(varNode->lv_Value, newVar->lv_Value, varNode->lv_Len);
609 AddTail((struct List *)&toProcess->pr_LocalVars,
610 (struct Node *)newVar);
614 return TRUE;
617 #warning Q: Is there a better way to pass DOSBase to KillCurrentProcess ?
618 static struct DosLibrary *DOSBase;
620 static int SetDosBase(LIBBASETYPEPTR __DOSBase)
622 D(bug("SetDosBase\n"));
623 DOSBase = __DOSBase;
624 return TRUE;
627 /* At pri -1 so it is executed before the DosInit in dos_init.c */
628 ADD2INITLIB(SetDosBase, -1)
630 static void KillCurrentProcess(void)
632 struct Process *me;
634 me = (struct Process *)FindTask(NULL);
636 /* Call user defined exit function before shutting down. */
637 if (me->pr_ExitCode != NULL)
640 The Ralph Bebel's guru book says that pr_ExitCode
641 is passed the process return code in D0 and pr_ExitData in D1,
642 but the Matt Dillon's DICE C implementation of vfork shows that
643 those parameters are passed also on the stack.
645 The AROS macros for functions with register parameters don't
646 support both register and stack parameters at once, so we use
647 the stack only. This oughta be fixed somehow.
649 me->pr_ExitCode(me->pr_Task.tc_UserData, me->pr_ExitData);
652 P(kprintf("Deleting local variables\n"));
654 /* Clean up */
655 freeLocalVars(me, DOSBase);
657 P(kprintf("Closing input stream\n"));
659 if (me->pr_Flags & PRF_CLOSEINPUT)
661 Close(me->pr_CIS);
664 P(kprintf("Closing output stream\n"));
666 if (me->pr_Flags & PRF_CLOSEOUTPUT)
668 Close(me->pr_COS);
671 P(kprintf("Closing error stream\n"));
673 if (me->pr_Flags & PRF_CLOSEERROR)
675 Close(me->pr_CES);
678 P(kprintf("Freeing arguments\n"));
680 if (me->pr_Flags & PRF_FREEARGS)
682 FreeVec(me->pr_Arguments);
685 P(kprintf("Unloading segment\n"));
687 if (me->pr_Flags & PRF_FREESEGLIST)
689 UnLoadSeg(me->pr_SegList);
692 P(kprintf("Unlocking current dir\n"));
694 if (me->pr_Flags & PRF_FREECURRDIR)
696 UnLock(me->pr_CurrentDir);
699 P(kprintf("Unlocking home dir\n"));
700 UnLock(me->pr_HomeDir);
702 P(kprintf("Freeing cli structure\n"));
704 if (me->pr_Flags & PRF_FREECLI)
706 FreeDosObject(DOS_CLI, BADDR(me->pr_CLI));
709 /* To implement NP_Synchronous and NP_NotifyOnDeath I need Child***()
710 here */
712 // if(me->pr_Flags & PRF_NOTIFYONDEATH)
713 // Signal(GetETask(me)->iet_Parent, SIGF_CHILD);
715 if (me->pr_Flags & PRF_SYNCHRONOUS)
717 P(kprintf("Calling ChildFree()\n"));
719 // ChildStatus(me);
720 internal_ChildFree(me, DOSBase);
723 removefromrootnode(me, DOSBase);
725 RemTask(NULL);
727 CloseLibrary((struct Library * )DOSBase);