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 */
115 /*23 */ { NP_NotifyOnDeath
, (IPTR
)FALSE
},
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();
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
)
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
))
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
;
181 namesize
= s
- (STRPTR
)defaults
[10].ti_Data
;
183 name
= AllocMem(namesize
, MEMF_PUBLIC
);
184 ENOMEM_IF(name
== NULL
);
187 s
= (STRPTR
)defaults
[12].ti_Data
;
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
),
200 ENOMEM_IF(memlist
== NULL
);
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();
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
;
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]);
248 oldpath
= BADDR(oldpath
[0]);
255 if (defaults
[2].ti_Data
== TAGDATA_NOT_SPECIFIED
)
257 if (__is_process(me
))
259 input
= Open("NIL:", MODE_OLDFILE
);
262 defaults
[2].ti_Data
= (IPTR
)input
;
266 defaults
[2].ti_Data
= 0;
272 if (defaults
[4].ti_Data
== TAGDATA_NOT_SPECIFIED
)
274 if (__is_process(me
))
276 output
= Open("NIL:", MODE_NEWFILE
);
279 defaults
[4].ti_Data
= (IPTR
)output
;
283 defaults
[4].ti_Data
= 0;
289 if (defaults
[6].ti_Data
== TAGDATA_NOT_SPECIFIED
)
291 if (__is_process(me
))
293 ces
= Open("NIL:", MODE_NEWFILE
);
296 defaults
[6].ti_Data
= (IPTR
)ces
;
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);
312 if (defaults
[8].ti_Data
== TAGDATA_NOT_SPECIFIED
)
314 if (__is_process(me
) && me
->pr_CurrentDir
)
316 curdir
= Lock("", SHARED_LOCK
);
319 defaults
[8].ti_Data
= (IPTR
)curdir
;
323 defaults
[8].ti_Data
= 0;
329 if (defaults
[21].ti_Data
== TAGDATA_NOT_SPECIFIED
)
331 defaults
[21].ti_Data
= 0;
333 if (__is_process(me
))
337 homedir
= DupLock(me
->pr_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 (defaults
[23].ti_Data
? PRF_NOTIFYONDEATH
: 0) |
403 PRF_FREEARGS
| PRF_FREECURRDIR
;
404 process
->pr_ExitCode
= (APTR
)defaults
[15].ti_Data
;
405 process
->pr_ExitData
= defaults
[16].ti_Data
;
406 process
->pr_Arguments
= argptr
;
408 if ((BOOL
)defaults
[18].ti_Data
) /* NP_CopyVars */
410 BOOL res
= copyVars(me
, process
, DOSBase
);
412 ENOMEM_IF(res
== FALSE
);
415 process
->pr_ShellPrivate
= 0;
418 if (defaults
[19].ti_Data
)
420 me
->pr_Flags
|= PRF_WAITINGFORCHILD
;
422 old_sig
= SetSignal(0L, SIGF_SINGLE
) & SIGF_SINGLE
;
425 /* argsize variable includes trailing 0 byte, but is supposed
428 if (argsize
) argsize
--;
435 process
, argptr
, argsize
,
436 defaults
[1].ti_Data
?
437 (APTR
)defaults
[1].ti_Data
:
438 (APTR
)((BPTR
*)BADDR(defaults
[0].ti_Data
) + 1),
439 KillCurrentProcess
, DOSBase
444 if (defaults
[19].ti_Data
)
446 P(kprintf("Calling ChildWait()\n"));
447 internal_ChildWait(FindTask(NULL
), DOSBase
);
448 P(kprintf("Returned from ChildWait()\n"));
456 if (__is_process(me
))
458 SetIoErr(ERROR_NO_FREE_STORE
);
461 freeLocalVars(process
, DOSBase
);
466 FreeDosObject(DOS_CLI
, cli
);
501 FreeMem(memlist
, sizeof(struct MemList
) + 2*sizeof(struct MemEntry
));
506 FreeMem(name
, namesize
);
511 FreeMem(stack
, defaults
[9].ti_Data
);
516 FreeMem(process
, sizeof(struct Process
));
523 if (defaults
[19].ti_Data
)
524 SetSignal(SIGF_SINGLE
, old_sig
);
529 } /* CreateNewProc */
535 static void freeLocalVars(struct Process
*process
, struct DosLibrary
*DOSBase
)
537 struct LocalVar
*varNode
;
538 struct Node
*tempNode
;
540 ForeachNodeSafe(&process
->pr_LocalVars
,
543 P(kprintf("Freeing variable %s with value %s at %p\n",
544 varNode
->lv_Node
.ln_Name
, varNode
->lv_Value
, varNode
));
545 FreeMem(varNode
->lv_Value
, varNode
->lv_Len
);
546 Remove((struct Node
*)varNode
);
552 void internal_ChildWait(struct Task
*task
, struct DosLibrary
* DOSBase
)
554 while (((struct Process
*)task
)->pr_Flags
& PRF_WAITINGFORCHILD
)
559 void internal_ChildFree(APTR tid
, struct DosLibrary
* DOSBase
)
561 struct Task
*task
= (struct Task
*)tid
;
562 struct Process
*parent
= (struct Process
*)(GetETask(task
)->et_Parent
);
564 D(bug("Awakening the parent task %p (called %s)\n", parent
, parent
->pr_Task
.tc_Node
.ln_Name
));
566 parent
->pr_Flags
&= ~PRF_WAITINGFORCHILD
;
567 Signal(&parent
->pr_Task
, SIGF_SINGLE
);
571 BOOL
copyVars(struct Process
*fromProcess
, struct Process
*toProcess
, struct DosLibrary
* DOSBase
)
573 /* We must have variables to copy... */
574 if (__is_process(fromProcess
))
576 struct LocalVar
*varNode
;
577 struct LocalVar
*newVar
;
579 /* We use the same strategy as in the ***Var() functions */
580 ForeachNode(&fromProcess
->pr_LocalVars
, varNode
)
582 LONG copyLength
= strlen(varNode
->lv_Node
.ln_Name
) + 1 +
583 sizeof(struct LocalVar
);
585 newVar
= (struct LocalVar
*)AllocVec(copyLength
,
586 MEMF_PUBLIC
| MEMF_CLEAR
);
590 CopyMem(varNode
, newVar
, copyLength
);
591 newVar
->lv_Node
.ln_Name
= (char *)newVar
+
592 sizeof(struct LocalVar
);
593 P(kprintf("Variable with name %s copied.\n",
594 newVar
->lv_Node
.ln_Name
));
598 newVar
->lv_Value
= AllocMem(varNode
->lv_Len
, MEMF_PUBLIC
);
600 if (newVar
->lv_Value
== NULL
)
602 /* Free variable node before shutting down */
608 CopyMem(varNode
->lv_Value
, newVar
->lv_Value
, varNode
->lv_Len
);
611 AddTail((struct List
*)&toProcess
->pr_LocalVars
,
612 (struct Node
*)newVar
);
619 #warning Q: Is there a better way to pass DOSBase to KillCurrentProcess ?
620 static struct DosLibrary
*DOSBase
;
622 static int SetDosBase(LIBBASETYPEPTR __DOSBase
)
624 D(bug("SetDosBase\n"));
629 /* At pri -1 so it is executed before the DosInit in dos_init.c */
630 ADD2INITLIB(SetDosBase
, -1)
632 static void KillCurrentProcess(void)
636 me
= (struct Process
*)FindTask(NULL
);
638 /* Call user defined exit function before shutting down. */
639 if (me
->pr_ExitCode
!= NULL
)
642 The Ralph Bebel's guru book says that pr_ExitCode
643 is passed the process return code in D0 and pr_ExitData in D1,
644 but the Matt Dillon's DICE C implementation of vfork shows that
645 those parameters are passed also on the stack.
647 The AROS macros for functions with register parameters don't
648 support both register and stack parameters at once, so we use
649 the stack only. This oughta be fixed somehow.
651 me
->pr_ExitCode(me
->pr_Task
.tc_UserData
, me
->pr_ExitData
);
654 P(kprintf("Deleting local variables\n"));
657 freeLocalVars(me
, DOSBase
);
659 P(kprintf("Closing input stream\n"));
661 if (me
->pr_Flags
& PRF_CLOSEINPUT
)
666 P(kprintf("Closing output stream\n"));
668 if (me
->pr_Flags
& PRF_CLOSEOUTPUT
)
673 P(kprintf("Closing error stream\n"));
675 if (me
->pr_Flags
& PRF_CLOSEERROR
)
680 P(kprintf("Freeing arguments\n"));
682 if (me
->pr_Flags
& PRF_FREEARGS
)
684 FreeVec(me
->pr_Arguments
);
687 P(kprintf("Unloading segment\n"));
689 if (me
->pr_Flags
& PRF_FREESEGLIST
)
691 UnLoadSeg(me
->pr_SegList
);
694 P(kprintf("Unlocking current dir\n"));
696 if (me
->pr_Flags
& PRF_FREECURRDIR
)
698 UnLock(me
->pr_CurrentDir
);
701 P(kprintf("Unlocking home dir\n"));
702 UnLock(me
->pr_HomeDir
);
704 P(kprintf("Freeing cli structure\n"));
706 if (me
->pr_Flags
& PRF_FREECLI
)
708 FreeDosObject(DOS_CLI
, BADDR(me
->pr_CLI
));
711 /* To implement NP_Synchronous and NP_NotifyOnDeath I need Child***()
714 // if(me->pr_Flags & PRF_NOTIFYONDEATH)
715 // Signal(GetETask(me)->iet_Parent, SIGF_CHILD);
717 if (me
->pr_Flags
& PRF_SYNCHRONOUS
)
719 P(kprintf("Calling ChildFree()\n"));
722 internal_ChildFree(me
, DOSBase
);
725 removefromrootnode(me
, DOSBase
);
729 CloseLibrary((struct Library
* )DOSBase
);