2 Copyright © 1995-2006, 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 *****************************************************************************/
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
);
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 */
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 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
427 if (argsize
) argsize
--;
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
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"));
455 if (__is_process(me
))
457 SetIoErr(ERROR_NO_FREE_STORE
);
460 freeLocalVars(process
, DOSBase
);
465 FreeDosObject(DOS_CLI
, cli
);
500 FreeMem(memlist
, sizeof(struct MemList
) + 2*sizeof(struct MemEntry
));
505 FreeMem(name
, namesize
);
510 FreeMem(stack
, defaults
[9].ti_Data
);
515 FreeMem(process
, sizeof(struct Process
));
522 if (defaults
[19].ti_Data
)
523 SetSignal(SIGF_SINGLE
, old_sig
);
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
,
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
);
551 void internal_ChildWait(struct Task
*task
, struct DosLibrary
* DOSBase
)
553 while (((struct Process
*)task
)->pr_Flags
& PRF_WAITINGFORCHILD
)
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
);
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
));
597 newVar
->lv_Value
= AllocMem(varNode
->lv_Len
, MEMF_PUBLIC
);
599 if (newVar
->lv_Value
== NULL
)
601 /* Free variable node before shutting down */
607 CopyMem(varNode
->lv_Value
, newVar
->lv_Value
, varNode
->lv_Len
);
610 AddTail((struct List
*)&toProcess
->pr_LocalVars
,
611 (struct Node
*)newVar
);
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"));
628 /* At pri -1 so it is executed before the DosInit in dos_init.c */
629 ADD2INITLIB(SetDosBase
, -1)
631 static void KillCurrentProcess(void)
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"));
656 freeLocalVars(me
, DOSBase
);
658 P(kprintf("Closing input stream\n"));
660 if (me
->pr_Flags
& PRF_CLOSEINPUT
)
665 P(kprintf("Closing output stream\n"));
667 if (me
->pr_Flags
& PRF_CLOSEOUTPUT
)
672 P(kprintf("Closing error stream\n"));
674 if (me
->pr_Flags
& PRF_CLOSEERROR
)
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***()
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"));
721 internal_ChildFree(me
, DOSBase
);
724 removefromrootnode(me
, DOSBase
);
728 CloseLibrary((struct Library
* )DOSBase
);