Support rastport clipping rectangle for layerless rastports
[tangerine.git] / rom / dos / createnewproc.c
blob84b29eccb66ed978af8fab7f2474b6996ebb2cc3
1 /*
2 Copyright © 1995-2006, 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
74 AROS_LIBBASE_EXT_DECL(struct DosLibrary *,DOSBase)
76 /* Allocated resources */
77 struct Process *process = NULL;
78 BPTR input = 0, output = 0, ces = 0, curdir = 0, homedir = 0;
79 STRPTR stack = NULL, name = NULL, argptr = NULL;
80 ULONG namesize = 0, argsize = 0;
81 struct MemList *memlist = NULL;
82 struct CommandLineInterface *cli = NULL;
83 struct Process *me = (struct Process *)FindTask(NULL);
84 STRPTR s;
85 ULONG old_sig;
87 /* TODO: NP_CommandName, NP_ConsoleTask, NP_NotifyOnDeath */
89 #define TAGDATA_NOT_SPECIFIED ~0ul
91 struct TagItem defaults[]=
93 /* 0 */ { NP_Seglist , 0 },
94 /* 1 */ { NP_Entry , (IPTR)NULL },
95 /* 2 */ { NP_Input , TAGDATA_NOT_SPECIFIED },
96 /* 3 */ { NP_CloseInput , 1 },
97 /* 4 */ { NP_Output , TAGDATA_NOT_SPECIFIED },
98 /* 5 */ { NP_CloseOutput , 1 },
99 /* 6 */ { NP_Error , TAGDATA_NOT_SPECIFIED },
100 /* 7 */ { NP_CloseError , 1 },
101 /* 8 */ { NP_CurrentDir , TAGDATA_NOT_SPECIFIED },
102 /* 9 */ { NP_StackSize , AROS_STACKSIZE },
103 /*10 */ { NP_Name , (IPTR)"New Process" },
104 /*11 */ { NP_Priority , me->pr_Task.tc_Node.ln_Pri },
105 /*12 */ { NP_Arguments , (IPTR)NULL },
106 /*13 */ { NP_Cli , 0 },
107 /*14 */ { NP_UserData , (IPTR)NULL },
108 /*15 */ { NP_ExitCode , (IPTR)NULL },
109 /*16 */ { NP_ExitData , (IPTR)NULL },
110 /*17 */ { NP_WindowPtr , (IPTR)NULL }, /* Default: default public screen */
111 /*18 */ { NP_CopyVars , (IPTR)TRUE },
112 /*19 */ { NP_Synchronous , (IPTR)FALSE },
113 /*20 */ { NP_FreeSeglist , (IPTR)TRUE },
114 /*21 */ { NP_HomeDir , TAGDATA_NOT_SPECIFIED },
115 /*22 */ { NP_Path , TAGDATA_NOT_SPECIFIED }, /* Default: copy path from parent */
116 { TAG_END , 0 }
119 /* C has no exceptions. This is a simple replacement. */
120 #define ERROR_IF(a) if(a) goto error /* Throw a generic error. */
121 #define ENOMEM_IF(a) if (a) goto enomem /* Throw out of memory. */
122 /* Inherit the parent process' stacksize if possible */
123 if (__is_process(me))
125 struct CommandLineInterface *cli = Cli();
127 if (cli)
129 LONG parentstack = cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT;
131 if (parentstack > AROS_STACKSIZE)
133 defaults[9].ti_Data = parentstack;
138 ApplyTagChanges(defaults, tags);
140 /* If both the seglist and the entry are specified, make sure that the entry resides in the seglist */
141 if (defaults[0].ti_Data && defaults[1].ti_Data)
143 BPTR seg;
145 for (seg = (BPTR) defaults[0].ti_Data; seg; seg = *(BPTR *)BADDR(seg))
149 (UBYTE *)defaults[1].ti_Data >= (UBYTE *)BADDR(seg) &&
150 (UBYTE *)defaults[1].ti_Data <= ((UBYTE *)BADDR(seg) + *((ULONG *)BADDR(seg) - 1) - sizeof(BPTR))
153 break;
157 if (!seg)
158 return NULL;
161 process = (struct Process *)AllocMem(sizeof(struct Process),
162 MEMF_PUBLIC | MEMF_CLEAR);
163 ENOMEM_IF(process == NULL);
165 /* Do this early to ease implementation of failure code */
166 NEWLIST((struct List *)&process->pr_LocalVars);
168 /* We need a minimum stack to handle interrupt contexts */
169 if (defaults[9].ti_Data < AROS_STACKSIZE)
171 defaults[9].ti_Data = AROS_STACKSIZE;
174 stack = AllocMem(defaults[9].ti_Data, MEMF_PUBLIC);
175 ENOMEM_IF(stack == NULL);
177 s = (STRPTR)defaults[10].ti_Data;
179 while (*s++);
181 namesize = s - (STRPTR)defaults[10].ti_Data;
183 name = AllocMem(namesize, MEMF_PUBLIC);
184 ENOMEM_IF(name == NULL);
186 /* NP_Arguments */
187 s = (STRPTR)defaults[12].ti_Data;
189 if (s != NULL)
191 while(*s++);
193 argsize = s - (STRPTR)defaults[12].ti_Data;
194 argptr = (STRPTR)AllocVec(argsize, MEMF_PUBLIC);
195 ENOMEM_IF(argptr == NULL);
198 memlist = AllocMem(sizeof(struct MemList) + 2*sizeof(struct MemEntry),
199 MEMF_ANY);
200 ENOMEM_IF(memlist == NULL);
202 /* NP_Cli */
203 if (defaults[13].ti_Data != 0)
205 BPTR *oldpath = NULL;
207 /* Don't forget to pass tags to AllocDosObject() */
208 cli = (struct CommandLineInterface *)AllocDosObject(DOS_CLI, tags);
209 ENOMEM_IF(cli == NULL);
211 cli->cli_DefaultStack = (defaults[9].ti_Data + CLI_DEFAULTSTACK_UNIT - 1) / CLI_DEFAULTSTACK_UNIT;
213 if (__is_process(me))
215 struct CommandLineInterface *oldcli = Cli();
217 if (oldcli != NULL)
219 LONG oldlen = AROS_BSTR_strlen(oldcli->cli_Prompt);
220 LONG newlen = GetTagData(ADO_PromptLen, 255, tags);
222 oldpath = BADDR(oldcli->cli_CommandDir);
224 CopyMem(BADDR(oldcli->cli_Prompt), BADDR(cli->cli_Prompt), (newlen<oldlen?newlen:oldlen) + 1);
229 if (defaults[22].ti_Data != TAGDATA_NOT_SPECIFIED)
231 cli->cli_CommandDir = (BPTR) defaults[22].ti_Data;
233 else
235 BPTR *nextpath,
236 *newpath = &cli->cli_CommandDir;
238 while (oldpath != NULL)
240 nextpath = AllocVec(2*sizeof(BPTR), MEMF_CLEAR);
241 ENOMEM_IF(nextpath == NULL);
243 newpath[0] = MKBADDR(nextpath);
244 nextpath[1] = DupLock(oldpath[1]);
245 ERROR_IF(!nextpath[1]);
247 newpath = nextpath;
248 oldpath = BADDR(oldpath[0]);
253 /* NP_Input */
255 if (defaults[2].ti_Data == TAGDATA_NOT_SPECIFIED)
257 if (__is_process(me))
259 input = Open("NIL:", MODE_OLDFILE);
260 ERROR_IF(!input);
262 defaults[2].ti_Data = (IPTR)input;
264 else
266 defaults[2].ti_Data = 0;
270 /* NP_Output */
272 if (defaults[4].ti_Data == TAGDATA_NOT_SPECIFIED)
274 if (__is_process(me))
276 output = Open("NIL:", MODE_NEWFILE);
277 ERROR_IF(!output);
279 defaults[4].ti_Data = (IPTR)output;
281 else
283 defaults[4].ti_Data = 0;
287 /* NP_Error */
289 if (defaults[6].ti_Data == TAGDATA_NOT_SPECIFIED)
291 if (__is_process(me))
293 ces = Open("NIL:", MODE_NEWFILE);
294 ERROR_IF(!ces);
296 defaults[6].ti_Data = (IPTR)ces;
298 else
300 defaults[6].ti_Data = 0;
304 if (defaults[6].ti_Data)
306 /* unbuffered to conform to widespread clib behavior */
307 SetVBuf((BPTR) defaults[6].ti_Data, NULL, BUF_NONE, -1);
310 /* NP_CurrentDir */
312 if (defaults[8].ti_Data == TAGDATA_NOT_SPECIFIED)
314 if (__is_process(me) && me->pr_CurrentDir)
316 curdir = Lock("", SHARED_LOCK);
317 ERROR_IF(!curdir);
319 defaults[8].ti_Data = (IPTR)curdir;
321 else
323 defaults[8].ti_Data = 0;
327 /* NP_HomeDir */
329 if (defaults[21].ti_Data == TAGDATA_NOT_SPECIFIED)
331 defaults[21].ti_Data = 0;
333 if (__is_process(me))
335 if (me->pr_HomeDir)
337 homedir = DupLock(me->pr_HomeDir);
338 ERROR_IF(!homedir);
340 defaults[21].ti_Data = (IPTR)homedir;
345 CopyMem((APTR)defaults[10].ti_Data, name, namesize);
346 CopyMem((APTR)defaults[12].ti_Data, argptr, argsize);
348 process->pr_Task.tc_Node.ln_Type = NT_PROCESS;
349 process->pr_Task.tc_Node.ln_Name = name;
350 process->pr_Task.tc_Node.ln_Pri = defaults[11].ti_Data;
351 process->pr_Task.tc_SPLower = stack;
352 process->pr_Task.tc_SPUpper = stack + defaults[9].ti_Data;
354 /* process->pr_ReturnAddr; */
355 NEWLIST(&process->pr_Task.tc_MemEntry);
357 memlist->ml_NumEntries = 3;
358 memlist->ml_ME[0].me_Addr = process;
359 memlist->ml_ME[0].me_Length = sizeof(struct Process);
360 memlist->ml_ME[1].me_Addr = stack;
361 memlist->ml_ME[1].me_Length = defaults[9].ti_Data;
362 memlist->ml_ME[2].me_Addr = name;
363 memlist->ml_ME[2].me_Length = namesize;
365 AddHead(&process->pr_Task.tc_MemEntry, &memlist->ml_Node);
367 process->pr_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
368 process->pr_MsgPort.mp_Flags = PA_SIGNAL;
369 process->pr_MsgPort.mp_SigBit = SIGB_DOS;
370 process->pr_MsgPort.mp_SigTask = process;
372 NEWLIST(&process->pr_MsgPort.mp_MsgList);
374 process->pr_SegList = (BPTR)defaults[0].ti_Data;
375 process->pr_StackSize = defaults[9].ti_Data;
376 process->pr_GlobVec = NULL; /* Unused BCPL crap */
377 process->pr_StackBase = MKBADDR(process->pr_Task.tc_SPUpper);
378 process->pr_Result2 = 0;
379 process->pr_CurrentDir = (BPTR)defaults[8].ti_Data;
380 process->pr_CIS = (BPTR)defaults[2].ti_Data;
381 process->pr_COS = (BPTR)defaults[4].ti_Data;
382 process->pr_CES = (BPTR)defaults[6].ti_Data;
383 process->pr_Task.tc_UserData = (APTR)defaults[14].ti_Data;
385 /* process->pr_ConsoleTask=; */
386 /* process->pr_FileSystemTask=; */
387 process->pr_CLI = MKBADDR(cli);
389 /* Set the name of this program */
390 internal_SetProgramName(cli, name, DOSBase);
391 D(bug("Calling internal_SetProgramName() with name = %s\n", name));
393 process->pr_PktWait = NULL;
394 process->pr_WindowPtr = (struct Window *)defaults[17].ti_Data;
395 process->pr_HomeDir = (BPTR)defaults[21].ti_Data;
396 process->pr_Flags = (defaults[3].ti_Data ? PRF_CLOSEINPUT : 0) |
397 (defaults[5].ti_Data ? PRF_CLOSEOUTPUT : 0) |
398 (defaults[7].ti_Data ? PRF_CLOSEERROR : 0) |
399 (defaults[13].ti_Data ? PRF_FREECLI : 0) |
400 (defaults[19].ti_Data ? PRF_SYNCHRONOUS : 0) |
401 (defaults[20].ti_Data ? PRF_FREESEGLIST : 0) |
402 PRF_FREEARGS | PRF_FREECURRDIR;
403 process->pr_ExitCode = (APTR)defaults[15].ti_Data;
404 process->pr_ExitData = defaults[16].ti_Data;
405 process->pr_Arguments = argptr;
407 if ((BOOL)defaults[18].ti_Data) /* NP_CopyVars */
409 BOOL res = copyVars(me, process, DOSBase);
411 ENOMEM_IF(res == FALSE);
414 process->pr_ShellPrivate = 0;
417 if (defaults[19].ti_Data)
419 me->pr_Flags |= PRF_WAITINGFORCHILD;
421 old_sig = SetSignal(0L, SIGF_SINGLE) & SIGF_SINGLE;
424 /* argsize variable includes trailing 0 byte, but is supposed
425 not to. */
427 if (argsize) argsize--;
432 AddProcess
434 process, argptr, argsize,
435 defaults[1].ti_Data ?
436 (APTR)defaults[1].ti_Data:
437 (APTR)((BPTR *)BADDR(defaults[0].ti_Data) + 1),
438 KillCurrentProcess, DOSBase
442 /* NP_Synchronous */
443 if (defaults[19].ti_Data)
445 P(kprintf("Calling ChildWait()\n"));
446 internal_ChildWait(FindTask(NULL), DOSBase);
447 P(kprintf("Returned from ChildWait()\n"));
450 goto end;
453 /* Fall through */
454 enomem:
455 if (__is_process(me))
457 SetIoErr(ERROR_NO_FREE_STORE);
460 freeLocalVars(process, DOSBase);
462 error:
463 if (cli != NULL)
465 FreeDosObject(DOS_CLI, cli);
468 if (homedir != NULL)
470 UnLock(homedir);
473 if (curdir != NULL)
475 UnLock(curdir);
478 if (output != NULL)
480 Close(output);
483 if (input != NULL)
485 Close(input);
488 if (ces != NULL)
490 Close(ces);
493 if (argptr != NULL)
495 FreeVec(argptr);
498 if (memlist != NULL)
500 FreeMem(memlist, sizeof(struct MemList) + 2*sizeof(struct MemEntry));
503 if (name != NULL)
505 FreeMem(name, namesize);
508 if (stack != NULL)
510 FreeMem(stack, defaults[9].ti_Data);
513 if (process != NULL)
515 FreeMem(process, sizeof(struct Process));
517 process = NULL;
520 end:
522 if (defaults[19].ti_Data)
523 SetSignal(SIGF_SINGLE, old_sig);
525 return process;
527 AROS_LIBFUNC_EXIT
528 } /* CreateNewProc */
534 static void freeLocalVars(struct Process *process, struct DosLibrary *DOSBase)
536 struct LocalVar *varNode;
537 struct Node *tempNode;
539 ForeachNodeSafe(&process->pr_LocalVars,
540 varNode, tempNode)
542 P(kprintf("Freeing variable %s with value %s at %p\n",
543 varNode->lv_Node.ln_Name, varNode->lv_Value, varNode));
544 FreeMem(varNode->lv_Value, varNode->lv_Len);
545 Remove((struct Node *)varNode);
546 FreeVec(varNode);
551 void internal_ChildWait(struct Task *task, struct DosLibrary * DOSBase)
553 while (((struct Process *)task)->pr_Flags & PRF_WAITINGFORCHILD)
554 Wait(SIGF_SINGLE);
558 void internal_ChildFree(APTR tid, struct DosLibrary * DOSBase)
560 struct Task *task = (struct Task *)tid;
561 struct Process *parent = (struct Process *)(GetETask(task)->et_Parent);
563 D(bug("Awakening the parent task %p (called %s)\n", parent, parent->pr_Task.tc_Node.ln_Name));
565 parent->pr_Flags &= ~PRF_WAITINGFORCHILD;
566 Signal(&parent->pr_Task, SIGF_SINGLE);
570 BOOL copyVars(struct Process *fromProcess, struct Process *toProcess, struct DosLibrary * DOSBase)
572 /* We must have variables to copy... */
573 if (__is_process(fromProcess))
575 struct LocalVar *varNode;
576 struct LocalVar *newVar;
578 /* We use the same strategy as in the ***Var() functions */
579 ForeachNode(&fromProcess->pr_LocalVars, varNode)
581 LONG copyLength = strlen(varNode->lv_Node.ln_Name) + 1 +
582 sizeof(struct LocalVar);
584 newVar = (struct LocalVar *)AllocVec(copyLength,
585 MEMF_PUBLIC | MEMF_CLEAR);
586 if (newVar == NULL)
587 return FALSE;
589 CopyMem(varNode, newVar, copyLength);
590 newVar->lv_Node.ln_Name = (char *)newVar +
591 sizeof(struct LocalVar);
592 P(kprintf("Variable with name %s copied.\n",
593 newVar->lv_Node.ln_Name));
595 if (varNode->lv_Len)
597 newVar->lv_Value = AllocMem(varNode->lv_Len, MEMF_PUBLIC);
599 if (newVar->lv_Value == NULL)
601 /* Free variable node before shutting down */
602 FreeVec(newVar);
604 return FALSE;
607 CopyMem(varNode->lv_Value, newVar->lv_Value, varNode->lv_Len);
610 AddTail((struct List *)&toProcess->pr_LocalVars,
611 (struct Node *)newVar);
615 return TRUE;
618 #warning Q: Is there a better way to pass DOSBase to KillCurrentProcess ?
619 static struct DosLibrary *DOSBase;
621 static int SetDosBase(LIBBASETYPEPTR __DOSBase)
623 D(bug("SetDosBase\n"));
624 DOSBase = __DOSBase;
625 return TRUE;
628 /* At pri -1 so it is executed before the DosInit in dos_init.c */
629 ADD2INITLIB(SetDosBase, -1)
631 static void KillCurrentProcess(void)
633 struct Process *me;
635 me = (struct Process *)FindTask(NULL);
637 /* Call user defined exit function before shutting down. */
638 if (me->pr_ExitCode != NULL)
641 The Ralph Bebel's guru book says that pr_ExitCode
642 is passed the process return code in D0 and pr_ExitData in D1,
643 but the Matt Dillon's DICE C implementation of vfork shows that
644 those parameters are passed also on the stack.
646 The AROS macros for functions with register parameters don't
647 support both register and stack parameters at once, so we use
648 the stack only. This oughta be fixed somehow.
650 me->pr_ExitCode(me->pr_Task.tc_UserData, me->pr_ExitData);
653 P(kprintf("Deleting local variables\n"));
655 /* Clean up */
656 freeLocalVars(me, DOSBase);
658 P(kprintf("Closing input stream\n"));
660 if (me->pr_Flags & PRF_CLOSEINPUT)
662 Close(me->pr_CIS);
665 P(kprintf("Closing output stream\n"));
667 if (me->pr_Flags & PRF_CLOSEOUTPUT)
669 Close(me->pr_COS);
672 P(kprintf("Closing error stream\n"));
674 if (me->pr_Flags & PRF_CLOSEERROR)
676 Close(me->pr_CES);
679 P(kprintf("Freeing arguments\n"));
681 if (me->pr_Flags & PRF_FREEARGS)
683 FreeVec(me->pr_Arguments);
686 P(kprintf("Unloading segment\n"));
688 if (me->pr_Flags & PRF_FREESEGLIST)
690 UnLoadSeg(me->pr_SegList);
693 P(kprintf("Unlocking current dir\n"));
695 if (me->pr_Flags & PRF_FREECURRDIR)
697 UnLock(me->pr_CurrentDir);
700 P(kprintf("Unlocking home dir\n"));
701 UnLock(me->pr_HomeDir);
703 P(kprintf("Freeing cli structure\n"));
705 if (me->pr_Flags & PRF_FREECLI)
707 FreeDosObject(DOS_CLI, BADDR(me->pr_CLI));
710 /* To implement NP_Synchronous and NP_NotifyOnDeath I need Child***()
711 here */
713 // if(me->pr_Flags & PRF_NOTIFYONDEATH)
714 // Signal(GetETask(me)->iet_Parent, SIGF_CHILD);
716 if (me->pr_Flags & PRF_SYNCHRONOUS)
718 P(kprintf("Calling ChildFree()\n"));
720 // ChildStatus(me);
721 internal_ChildFree(me, DOSBase);
724 removefromrootnode(me, DOSBase);
726 RemTask(NULL);
728 CloseLibrary((struct Library * )DOSBase);