2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
5 Loader for shared libraries and devices.
8 #include <aros/asmcall.h>
9 #include <aros/debug.h>
10 #include <aros/symbolsets.h>
11 #include <exec/execbase.h>
12 #include <exec/resident.h>
13 #include <exec/memory.h>
14 #include <exec/errors.h>
15 #include <exec/devices.h>
16 #include <exec/ports.h>
17 #include <exec/alerts.h>
18 #include <exec/tasks.h>
20 #include <dos/dosextens.h>
21 #include <dos/dostags.h>
22 #include <proto/exec.h>
23 #include <proto/dos.h>
30 #define CHECK_DEPENDENCY 1
32 /* Please leave them here! They are needed on Linux-M68K */
33 AROS_LD2(struct Library
*, OpenLibrary
,
34 AROS_LDA(STRPTR
, libname
, A1
),
35 AROS_LDA(ULONG
, version
, D0
),
36 struct ExecBase
*, SysBase
, 0, Dos
);
37 AROS_LD4(LONG
, OpenDevice
,
38 AROS_LDA(STRPTR
, devname
, A0
),
39 AROS_LDA(IPTR
, unitNumber
, D0
),
40 AROS_LDA(struct IORequest
*, iORequest
, A1
),
41 AROS_LDA(ULONG
, flags
, D1
),
42 struct ExecBase
*, SysBase
, 0, Dos
);
43 AROS_LD1(void, CloseLibrary
,
44 AROS_LDA(struct Library
*, library
, A1
),
45 struct ExecBase
*, SysBase
, 0, Dos
);
46 AROS_LD1(void, CloseDevice
,
47 AROS_LDA(struct IORequest
*, iORequest
, A1
),
48 struct ExecBase
*, SysBase
, 0, Dos
);
49 AROS_LD1(void, RemLibrary
,
50 AROS_LDA(struct Library
*, library
, A1
),
51 struct ExecBase
*, SysBase
, 0, Dos
);
55 struct Message ldd_Msg
; /* Message link */
56 struct MsgPort ldd_ReplyPort
; /* Callers ReplyPort */
58 STRPTR ldd_Name
; /* Name of thing to load */
60 STRPTR ldd_BaseDir
; /* Base directory to load from */
61 BPTR ldd_Return
; /* Loaded seglist */
64 static const char ldDemonName
[] = "Lib & Dev Loader Daemon";
67 BPTR LDLoad( caller, name, basedir, DOSBase )
68 Try and load a segment from disk for the object <name>, relative
69 to directory <basedir>. Will also try <caller>'s current and home
72 static BPTR
LDLoad(struct Process
*caller
, STRPTR name
, STRPTR basedir
,
73 struct Library
*DOSBase
, struct ExecBase
*SysBase
)
75 struct Process
*me
= (struct Process
*)FindTask(NULL
);
82 If the caller was a process, we have more scope for loading
83 libraries. We can load them from the callers current directory,
84 or from the PROGDIR: assign. These could both be the same
88 "[LDLoad] caller=(%p) %s, name=%s, basedir=%s\n",
89 caller
, caller
->pr_Task
.tc_Node
.ln_Name
, name
, basedir
92 if (strncmp(name
, "PROGDIR:", 8) == 0)
94 /* Special case for explicit PROGDIR-based path */
95 if (caller
->pr_Task
.tc_Node
.ln_Type
== NT_PROCESS
)
97 if (caller
->pr_HomeDir
!= BNULL
)
99 BPTR oldHomeDir
= me
->pr_HomeDir
;
100 D(bug("[LDLoad] Trying homedir\n"));
101 /* Temporarily override pr_HomeDir to let GetDeviceProc handle
102 PROGDIR: case correctly while opening library file */
103 me
->pr_HomeDir
= caller
->pr_HomeDir
;
104 seglist
= LoadSeg(name
);
105 me
->pr_HomeDir
= oldHomeDir
;
109 else if (!strstr(name
, ":")) {
110 delimPos
= strlen(basedir
);
111 pathLen
= delimPos
+ strlen(name
) + 2;
112 path
= AllocMem(pathLen
, MEMF_ANY
);
114 strcpy(path
, basedir
);
115 path
[delimPos
] = '/';
116 strcpy(&path
[delimPos
+ 1], name
);
118 if (caller
->pr_Task
.tc_Node
.ln_Type
== NT_PROCESS
)
120 /* Try the current directory of the caller */
122 D(bug("[LDLoad] Process\n"));
123 me
->pr_CurrentDir
= caller
->pr_CurrentDir
;
124 D(bug("[LDLoad] Trying currentdir\n"));
125 seglist
= LoadSeg(name
);
126 if ((!seglist
) && path
)
127 seglist
= LoadSeg(path
);
129 /* The the program directory of the caller */
130 if((!seglist
) && (caller
->pr_HomeDir
!= BNULL
))
132 D(bug("[LDLoad] Trying homedir\n"));
133 me
->pr_CurrentDir
= caller
->pr_HomeDir
;
134 seglist
= LoadSeg(name
);
135 if ((!seglist
) && path
)
136 seglist
= LoadSeg(path
);
142 /* Nup, let's try the default directory as supplied. */
143 D(bug("[LDLoad] Trying defaultdir\n"));
144 path
[delimPos
] = ':';
145 seglist
= LoadSeg(path
);
147 FreeMem(path
, pathLen
);
150 seglist
= LoadSeg(name
);
156 Library *LDInit(seglist, DOSBase)
157 Initialise the library.
159 static struct Library
*LDInit(BPTR seglist
, struct List
*list
, STRPTR resname
, struct ExecBase
*SysBase
)
161 struct Node
*node
= NULL
;
164 /* we may not have any extension fields */
165 const int sizeofresident
= offsetof(struct Resident
, rt_Init
) + sizeof(APTR
);
169 STRPTR addr
= (STRPTR
)((IPTR
)BADDR(seg
) - sizeof(ULONG
));
170 ULONG size
= *(ULONG
*)addr
;
173 addr
+= sizeof(BPTR
) + sizeof(ULONG
),
174 size
-= sizeof(BPTR
) + sizeof(ULONG
);
175 size
>= sizeofresident
;
177 // size -= AROS_PTRALIGN, addr += AROS_PTRALIGN
180 struct Resident
*res
= (struct Resident
*)addr
;
181 if( res
->rt_MatchWord
== RTC_MATCHWORD
182 && res
->rt_MatchTag
== res
)
184 D(bug("[LDInit] Calling InitResident(%p) on %s\n", res
, res
->rt_Name
));
185 /* AOS compatibility requirement.
186 * Ramlib ignores InitResident() return code.
187 * After InitResident() it checks if lib/dev appeared
188 * in Exec lib/dev list via FindName().
190 * Evidently InitResident()'s return code was not
191 * reliable for some early AOS libraries.
194 InitResident(res
, seglist
);
195 node
= FindName(list
, res
->rt_Name
);
197 D(bug("[LDInit] Done calling InitResident(%p) on %s, seg %p, node %p\n", res
, res
->rt_Name
, BADDR(seglist
), node
));
199 return (struct Library
*)node
;
202 seg
= *(BPTR
*)BADDR(seg
);
204 D(bug("[LDInit] Couldn't find Resident for %p\n", seglist
));
206 /* If struct Resident was not found, just run the code. SegList in A0.
207 * Required to load WB1.x devs:narrator.device. */
209 AROS_UFC1NR(void, BADDR(seglist
) + sizeof(ULONG
), AROS_UFCA(BPTR
, seglist
, A0
));
210 node
= FindName(list
, resname
);
212 D(bug("[LDInit] Done direct calling %s, seg %p, node %p\n", resname
, BADDR(seglist
), node
));
214 return (struct Library
*)node
;
217 #define ExecOpenLibrary(libname, version) \
218 AROS_CALL2(struct Library *, ldBase->__OpenLibrary, \
219 AROS_LCA(STRPTR, libname, A1), \
220 AROS_LCA(ULONG, version, D0), \
221 struct ExecBase *, SysBase)
223 #define ExecOpenDevice(devname, unitNumber, iORequest, flags) \
224 AROS_CALL4(LONG, ldBase->__OpenDevice, \
225 AROS_LCA(STRPTR, devname, A0), \
226 AROS_LCA(IPTR, unitNumber, D0), \
227 AROS_LCA(struct IORequest *, iORequest, A1), \
228 AROS_LCA(ULONG, flags, D1), \
229 struct ExecBase *, SysBase)
233 struct Node ldon_Node
;
234 struct SignalSemaphore ldon_SigSem
;
235 ULONG ldon_AccessCount
;
237 struct Task
*ldon_FirstLocker
;
241 static struct LDObjectNode
*LDNewObjectNode(STRPTR name
, struct ExecBase
*SysBase
)
243 struct LDObjectNode
*ret
= AllocVec(sizeof(struct LDObjectNode
), MEMF_ANY
);
246 ULONG len
= strlen(name
);
247 STRPTR dupname
= AllocVec(len
+1, MEMF_ANY
);
250 CopyMem(name
, dupname
, len
);
252 ret
->ldon_Node
.ln_Name
= dupname
;
253 InitSemaphore(&ret
->ldon_SigSem
);
254 ret
->ldon_AccessCount
= 0;
257 ret
->ldon_FirstLocker
= FindTask(0);
268 static struct LDObjectNode
*LDRequestObject(STRPTR libname
, ULONG version
, STRPTR dir
, struct List
*list
, struct ExecBase
*SysBase
)
270 /* We use FilePart() because the liblist is built from resident IDs,
271 and contain no path. Eg. The user can request gadgets/foo.gadget,
272 but the resident only contains foo.gadget
274 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
275 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
276 STRPTR stripped_libname
= FilePart(libname
);
277 struct Library
*tmplib
;
278 struct LDObjectNode
*object
;
281 We get the DOS semaphore to prevent the following:
282 - task 1 tries to open foobar.library, needs to load it from disk...
283 - task 1 Permit()'s (since it's not doing list things)
284 - task switch (whilst LDDemon MAY get process next it might not)
285 - task 2 tries to open foobar.library, needs to load it from disk...
286 - it also requests LDDemon to open foobar.library, so it is now
287 trying to open it twice
289 We block all OpenLibrary() callers from searching the list until
290 all the other OpenLibrary() callers have returned. That way,
291 task #2 won't ask for foobar.library until task #1 has got its
292 response back from the LDDemon process.
294 falemagn: I changed the implementation of all that.
295 There's a list of "LDObjectNodes", that contain the name
296 of the object being opened. Since the problem is that more
297 processes can attempt to open the same device/library. Instead of
298 locking a global semaphore until the opening is done, we lock a
299 per-object semaphore, so that others libraries/devices can be opened
300 in the meantime. Before, a deadlock could happen if there was a
303 Process A opens L --------> LDDemon loads L and locks sem S
309 L spawns a process B and ----------> The process opens
310 waits for it to respond a library but gets loked
311 to a message <----/---- because sem S is locked
318 Hopefully this won't happen anymore now.
320 ObtainSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
322 object
= (struct LDObjectNode
*)FindName(&ldBase
->dl_LDObjectsList
, stripped_libname
);
325 object
= LDNewObjectNode(stripped_libname
, SysBase
);
328 AddTail(&ldBase
->dl_LDObjectsList
, (struct Node
*)object
);
334 object
->ldon_AccessCount
+= 1;
337 ReleaseSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
342 ObtainSemaphore(&object
->ldon_SigSem
);
344 /* Try to find the resident in the list */
345 tmplib
= (struct Library
*)FindName(list
, stripped_libname
);
349 /* Try to load from disk if not found */
352 ldd
.ldd_ReplyPort
.mp_SigBit
= SIGB_SINGLE
;
353 ldd
.ldd_ReplyPort
.mp_SigTask
= FindTask(NULL
);
354 ldd
.ldd_ReplyPort
.mp_Flags
= PA_SIGNAL
;
355 ldd
.ldd_ReplyPort
.mp_Node
.ln_Type
= NT_MSGPORT
;
357 NEWLIST(&ldd
.ldd_ReplyPort
.mp_MsgList
);
359 ldd
.ldd_Msg
.mn_Node
.ln_Type
= NT_MESSAGE
;
360 ldd
.ldd_Msg
.mn_Length
= sizeof(struct LDDMsg
);
361 ldd
.ldd_Msg
.mn_ReplyPort
= &ldd
.ldd_ReplyPort
;
363 ldd
.ldd_Name
= libname
;
364 ldd
.ldd_BaseDir
= dir
;
366 SetSignal(0, SIGF_SINGLE
);
367 D(bug("[LDCaller] Sending request for %s\n", stripped_libname
));
369 PutMsg(ldBase
->dl_LDDemonPort
, (struct Message
*)&ldd
);
370 WaitPort(&ldd
.ldd_ReplyPort
);
372 D(bug("[LDCaller] Returned 0x%p\n", ldd
.ldd_Return
));
376 tmplib
= LDInit(ldd
.ldd_Return
, list
, stripped_libname
, SysBase
);
378 UnLoadSeg(ldd
.ldd_Return
);
385 * The library is not on disk so check Resident List.
386 * It can be there if it is resident but was flushed.
388 struct Resident
*resident
= FindResident(stripped_libname
);
391 * Check if the resident is of required type and version is correct.
392 * This relies on the fact that lh_Type is set correctly for exec lists.
393 * In AROS this is true (see rom/exec/prepareexecbase.c).
395 if (resident
&& (resident
->rt_Type
== list
->lh_Type
) && (resident
->rt_Version
>= version
))
396 InitResident(resident
, BNULL
);
402 static void LDReleaseObject(struct LDObjectNode
*object
, struct ExecBase
*SysBase
)
404 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
407 Release the semaphore here, after calling Open vector. This
408 means that library open is singlethreaded by the semaphore.
409 It also handles circular dependant libraries. (Won't deadlock),
410 and recursive OpenLibrary calls (Semaphores nest when obtained
411 several times in a row by the same task).
414 ObtainSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
416 if (--(object
->ldon_AccessCount
) == 0)
418 Remove((struct Node
*)object
);
420 * CHECKME: In LDRequestObject() we obtain the object semaphore also on a new object.
421 * So shouldn't we release it here too ?
424 FreeVec(object
->ldon_Node
.ln_Name
);
428 ReleaseSemaphore(&object
->ldon_SigSem
);
430 ReleaseSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
433 AROS_LH2(struct Library
*, OpenLibrary
,
434 AROS_LHA(STRPTR
, libname
, A1
),
435 AROS_LHA(ULONG
, version
, D0
),
436 struct ExecBase
*, SysBase
, 0, Dos
)
440 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
441 struct Library
*library
;
442 struct LDObjectNode
*object
= LDRequestObject(libname
, version
, "libs", &SysBase
->LibList
, SysBase
);
447 /* Call the EXEC's OpenLibrary function */
448 library
= ExecOpenLibrary(object
->ldon_Node
.ln_Name
, version
);
450 LDReleaseObject(object
, SysBase
);
457 AROS_LH4(LONG
, OpenDevice
,
458 AROS_LHA(STRPTR
, devname
, A0
),
459 AROS_LHA(IPTR
, unitNumber
, D0
),
460 AROS_LHA(struct IORequest
*, iORequest
, A1
),
461 AROS_LHA(ULONG
, flags
, D1
),
462 struct ExecBase
*, SysBase
, 0, Dos
)
466 struct LDObjectNode
*object
;
467 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
469 object
= LDRequestObject(devname
, 0, "devs", &SysBase
->DeviceList
, SysBase
);
472 /* Call exec.library/OpenDevice(), it will do the job */
473 ExecOpenDevice(object
->ldon_Node
.ln_Name
, unitNumber
, iORequest
, flags
);
474 LDReleaseObject(object
, SysBase
);
478 iORequest
->io_Error
= IOERR_OPENFAIL
;
479 iORequest
->io_Device
= NULL
;
480 iORequest
->io_Unit
= NULL
;
483 D(bug("[LDCaller] Open result: %d\n", iORequest
->io_Error
));
485 return iORequest
->io_Error
;
490 AROS_LH1(void, CloseLibrary
,
491 AROS_LHA(struct Library
*, library
, A1
),
492 struct ExecBase
*, SysBase
, 0, Dos
)
496 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
497 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
500 if( library
!= NULL
)
503 seglist
= AROS_LVO_CALL0(BPTR
, struct Library
*, library
, 2, );
506 ldBase
->dl_LDReturn
= MEM_TRY_AGAIN
;
508 /* Safe to call from a Task */
517 AROS_LH1(void, CloseDevice
,
518 AROS_LHA(struct IORequest
*, iORequest
, A1
),
519 struct ExecBase
*, SysBase
, 0, Dos
)
522 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
523 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
524 BPTR seglist
= BNULL
;
527 if( iORequest
->io_Device
!= NULL
)
529 seglist
= AROS_LVO_CALL1(BPTR
,
530 AROS_LCA(struct IORequest
*, iORequest
, A1
),
531 struct Device
*, iORequest
->io_Device
, 2, );
532 iORequest
->io_Device
=(struct Device
*)-1;
535 ldBase
->dl_LDReturn
= MEM_TRY_AGAIN
;
543 AROS_LH1(void, RemLibrary
,
544 AROS_LHA(struct Library
*, library
, A1
),
545 struct ExecBase
*, SysBase
, 0, Dos
)
549 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
550 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
554 /* calling ExpungeLib: library ends up in D0 and A6 for compatibility */
555 seglist
= AROS_CALL1(BPTR
, __AROS_GETVECADDR(library
, 3),
556 AROS_LCA(struct Library
*, library
, D0
),
557 struct Library
*, library
561 ldBase
->dl_LDReturn
= MEM_TRY_AGAIN
;
569 AROS_UFH3(LONG
, LDFlush
,
570 AROS_UFHA(struct MemHandlerData
*, lmhd
, A0
),
571 AROS_UFHA(APTR
, data
, A1
),
572 AROS_UFHA(struct ExecBase
*, SysBase
, A6
)
577 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
578 struct Library
*library
;
580 D(bug("[LDDemon] Flush called\n"));
581 ldBase
->dl_LDReturn
= MEM_DID_NOTHING
;
583 /* Forbid() is already done, but I don't want to rely on it. */
586 /* Follow the linked list of shared libraries. */
587 library
= (struct Library
*)SysBase
->LibList
.lh_Head
;
588 while(library
->lib_Node
.ln_Succ
!= NULL
)
590 /* Flush libraries with a 0 open count */
591 if( ! library
->lib_OpenCnt
)
593 /* the library list node will be wiped from memory */
594 struct Library
*nextLib
= (struct Library
*)library
->lib_Node
.ln_Succ
;
596 /* Did it really go away? */
597 if( ldBase
->dl_LDReturn
!= MEM_DID_NOTHING
)
599 /* Yes! Return it. */
601 return MEM_TRY_AGAIN
;
607 /* Go on to next library. */
608 library
= (struct Library
*)library
->lib_Node
.ln_Succ
;
612 /* Do the same with the device list. */
613 library
= (struct Library
*)SysBase
->DeviceList
.lh_Head
;
614 while(library
->lib_Node
.ln_Succ
!= NULL
)
616 /* Flush libraries with a 0 open count */
617 if( ! library
->lib_OpenCnt
)
619 struct Library
*nextDev
= (struct Library
*)library
->lib_Node
.ln_Succ
;
620 RemDevice((struct Device
*)library
);
621 /* Did it really go away? */
622 if( ldBase
->dl_LDReturn
!= MEM_DID_NOTHING
)
624 /* Yes! Return it. */
626 return MEM_TRY_AGAIN
;
632 /* Go on to next library. */
633 library
= (struct Library
*)library
->lib_Node
.ln_Succ
;
637 return MEM_DID_NOTHING
;
644 The LDDemon process entry. Sits around and does nothing until a
645 request for a library comes, when it will then find the library
646 and hopefully open it.
648 static AROS_PROCH(LDDemon
, argptr
, argsize
, SysBase
)
652 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
653 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
658 WaitPort(ldBase
->dl_LDDemonPort
);
659 while( (ldd
= (struct LDDMsg
*)GetMsg(ldBase
->dl_LDDemonPort
)) )
661 D(bug("[LDDemon] Got a request for %s in %s\n", ldd
->ldd_Name
, ldd
->ldd_BaseDir
));
663 ldd
->ldd_Return
= LDLoad(ldd
->ldd_ReplyPort
.mp_SigTask
, ldd
->ldd_Name
, ldd
->ldd_BaseDir
, DOSBase
, SysBase
);
665 D(bug("[LDDemon] Replying with %p as result\n", ldd
->ldd_Return
));
666 ReplyMsg((struct Message
*)ldd
);
667 } /* messages available */
676 static ULONG
LDDemon_Init(struct LDDemonBase
*ldBase
)
678 struct Library
*DOSBase
;
679 struct TagItem tags
[] =
681 { NP_Entry
, (IPTR
)LDDemon
},
684 { NP_WindowPtr
, -1 },
685 { NP_Name
, (IPTR
)ldDemonName
},
690 DOSBase
= TaggedOpenLibrary(TAGGEDOPEN_DOS
);
692 Alert( AN_RAMLib
| AG_OpenLib
| AO_DOSLib
| AT_DeadEnd
);
695 if ((ldBase
->dl_LDDemonPort
= CreateMsgPort()) == NULL
)
697 Alert( AN_RAMLib
| AG_NoMemory
| AT_DeadEnd
);
700 FreeSignal(ldBase
->dl_LDDemonPort
->mp_SigBit
);
701 ldBase
->dl_LDDemonPort
->mp_SigBit
= SIGBREAKB_CTRL_F
;
703 ldBase
->dl_LDHandler
.is_Node
.ln_Name
= (STRPTR
)ldDemonName
;
704 ldBase
->dl_LDHandler
.is_Node
.ln_Pri
= 0;
705 ldBase
->dl_LDHandler
.is_Code
= (VOID_FUNC
)LDFlush
;
706 ldBase
->dl_LDHandler
.is_Data
= NULL
;
708 ldBase
->dl_DOSBase
= DOSBase
;
710 NEWLIST(&ldBase
->dl_LDObjectsList
);
711 InitSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
713 SysBase
->ex_RamLibPrivate
= ldBase
;
715 AddMemHandler(&ldBase
->dl_LDHandler
);
718 * Grab the semaphore ourself. The reason for this is that it will
719 * cause all other tasks to wait until we have finished initialising
720 * before they try and open something.
722 ObtainSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
724 #define SetFunc(offs,ptr) \
725 SetFunction(&SysBase->LibNode, (-offs)*(LONG)LIB_VECTSIZE, \
726 AROS_SLIB_ENTRY(ptr,Dos,0))
728 /* Do not set the vectors until you have initialised everything else. */
729 ldBase
->__OpenLibrary
= SetFunc(92, OpenLibrary
);
730 ldBase
->__OpenDevice
= SetFunc(74, OpenDevice
);
731 (void)SetFunc(69, CloseLibrary
);
732 (void)SetFunc(75, CloseDevice
);
733 (void)SetFunc(67, RemLibrary
);
734 (void)SetFunc(73, RemLibrary
);
736 if( !(ldBase
->dl_LDDemonTask
= CreateNewProc((struct TagItem
*)tags
)) )
738 Alert( AT_DeadEnd
| AN_RAMLib
| AG_ProcCreate
);
741 /* Fix up the MsgPort */
743 ldBase
->dl_LDDemonPort
->mp_SigTask
= ldBase
->dl_LDDemonTask
;
745 /* Then unlock the semaphore to allow other processes to run. */
746 ReleaseSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
751 ADD2INITLIB(LDDemon_Init
, 0)