2 Copyright © 1995-2007, The AROS Development Team. All rights reserved.
5 Desc: Create a new process
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
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>
39 /*****************************************************************************
42 #include <proto/dos.h>
44 AROS_LH1(struct Process
*, CreateNewProc
,
47 AROS_LHA(struct TagItem
*, tags
, D1
),
50 struct DosLibrary
*, DOSBase
, 83, Dos
)
53 Create a new process using the tagitem array.
56 tags - information on the new process.
59 Pointer to the new process or NULL on error.
71 *****************************************************************************/
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
);
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 */
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();
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
)
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
))
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
;
180 namesize
= s
- (STRPTR
)defaults
[10].ti_Data
;
182 name
= AllocMem(namesize
, MEMF_PUBLIC
);
183 ENOMEM_IF(name
== NULL
);
186 s
= (STRPTR
)defaults
[12].ti_Data
;
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
),
199 ENOMEM_IF(memlist
== NULL
);
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();
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
;
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]);
247 oldpath
= BADDR(oldpath
[0]);
254 if (defaults
[2].ti_Data
== TAGDATA_NOT_SPECIFIED
)
256 if (__is_process(me
))
258 input
= Open("NIL:", MODE_OLDFILE
);
261 defaults
[2].ti_Data
= (IPTR
)input
;
265 defaults
[2].ti_Data
= 0;
271 if (defaults
[4].ti_Data
== TAGDATA_NOT_SPECIFIED
)
273 if (__is_process(me
))
275 output
= Open("NIL:", MODE_NEWFILE
);
278 defaults
[4].ti_Data
= (IPTR
)output
;
282 defaults
[4].ti_Data
= 0;
288 if (defaults
[6].ti_Data
== TAGDATA_NOT_SPECIFIED
)
290 if (__is_process(me
))
292 ces
= Open("NIL:", MODE_NEWFILE
);
295 defaults
[6].ti_Data
= (IPTR
)ces
;
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);
311 if (defaults
[8].ti_Data
== TAGDATA_NOT_SPECIFIED
)
313 if (__is_process(me
) && me
->pr_CurrentDir
)
315 curdir
= Lock("", SHARED_LOCK
);
318 defaults
[8].ti_Data
= (IPTR
)curdir
;
322 defaults
[8].ti_Data
= 0;
328 if (defaults
[21].ti_Data
== TAGDATA_NOT_SPECIFIED
)
330 defaults
[21].ti_Data
= 0;
332 if (__is_process(me
))
336 homedir
= DupLock(me
->pr_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
426 if (argsize
) argsize
--;
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
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"));
454 if (__is_process(me
))
456 SetIoErr(ERROR_NO_FREE_STORE
);
459 freeLocalVars(process
, DOSBase
);
464 FreeDosObject(DOS_CLI
, cli
);
499 FreeMem(memlist
, sizeof(struct MemList
) + 2*sizeof(struct MemEntry
));
504 FreeMem(name
, namesize
);
509 FreeMem(stack
, defaults
[9].ti_Data
);
514 FreeMem(process
, sizeof(struct Process
));
521 if (defaults
[19].ti_Data
)
522 SetSignal(SIGF_SINGLE
, old_sig
);
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
,
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
);
550 void internal_ChildWait(struct Task
*task
, struct DosLibrary
* DOSBase
)
552 while (((struct Process
*)task
)->pr_Flags
& PRF_WAITINGFORCHILD
)
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
);
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
));
596 newVar
->lv_Value
= AllocMem(varNode
->lv_Len
, MEMF_PUBLIC
);
598 if (newVar
->lv_Value
== NULL
)
600 /* Free variable node before shutting down */
606 CopyMem(varNode
->lv_Value
, newVar
->lv_Value
, varNode
->lv_Len
);
609 AddTail((struct List
*)&toProcess
->pr_LocalVars
,
610 (struct Node
*)newVar
);
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"));
627 /* At pri -1 so it is executed before the DosInit in dos_init.c */
628 ADD2INITLIB(SetDosBase
, -1)
630 static void KillCurrentProcess(void)
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"));
655 freeLocalVars(me
, DOSBase
);
657 P(kprintf("Closing input stream\n"));
659 if (me
->pr_Flags
& PRF_CLOSEINPUT
)
664 P(kprintf("Closing output stream\n"));
666 if (me
->pr_Flags
& PRF_CLOSEOUTPUT
)
671 P(kprintf("Closing error stream\n"));
673 if (me
->pr_Flags
& PRF_CLOSEERROR
)
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***()
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"));
720 internal_ChildFree(me
, DOSBase
);
723 removefromrootnode(me
, DOSBase
);
727 CloseLibrary((struct Library
* )DOSBase
);