start service tasks separately in-case platforms need to perform additional set-up...
[AROS.git] / compiler / posixc / flock.c
blob07f0fac01d28cb5352ffe99c9f2a67a28b84ae5f
1 /*
2 Copyright © 2008-2013, The AROS Development Team. All rights reserved.
3 $Id$
5 4.4BSD function flock().
6 */
8 #define DEBUG 0
9 #include <proto/exec.h>
10 #include <exec/exec.h>
11 #include <proto/dos.h>
12 #include <dos/dos.h>
13 #include <aros/debug.h>
14 #include <aros/symbolsets.h>
16 #include <errno.h>
18 #include "__fdesc.h"
19 #include "__posixc_intbase.h"
21 struct FlockNode
23 struct Node node;
24 struct SignalSemaphore *sem;
27 LONG AddToList(struct SignalSemaphore *sem);
28 void RemoveFromList(struct SignalSemaphore *sem);
30 /*****************************************************************************
32 NAME */
33 #include <sys/file.h>
35 int flock (
37 /* SYNOPSIS */
38 int fd,
39 int operation)
41 /* FUNCTION
42 Apply or remove an advisory lock on open file descriptor fd. Operation
43 argument can be one of the following constants:
45 LOCK_SH - Place a shared lock on the file specified by fd. More that
46 one process can hold a shared lock on a given file at a
47 time.
49 LOCK_EX - Place an exclusive lock on the file specified by fd. Only
50 one process can hold an exclusive lock on a given file at
51 a time.
53 LOCK_UN - Remove an existing lock from the file specified by fd.
55 LOCK_EX operation blocks if there is a lock already placed on the
56 file. LOCK_SH blocks if there is an exclusive lock already placed
57 on the file. If you want to do a non-blocking request, OR the
58 operation specifier with LOCK_NB constant. In this case flock() will
59 return -1 instead of blocking and set errno to EWOULDBLOCK.
61 Advisory locks created with flock() are shared among duplicated file
62 descriptors.
64 INPUTS
65 fd - File descriptor of the file you want to place or remove lock from.
66 operation - Lock operation to be performed.
68 RESULT
69 0 on success, -1 on error. In case of error a global errno variable
70 is set.
72 NOTES
73 Locks placed with flock() are only advisory, they place no
74 restrictions to any file or file descriptor operations.
76 EXAMPLE
78 BUGS
79 It's currently possible to remove lock placed by another process.
81 SEE ALSO
83 INTERNALS
84 Since advisory locks semantics is equal to exec.library semaphores
85 semantics, semaphores are used to implement locks. For a given file
86 a semaphore named FLOCK(path) is created where path is a full path to
87 the file. Locks held by a given process are stored in
88 PosixCBase->file_locks and released during process exit.
90 ******************************************************************************/
92 fdesc *fdesc = __getfdesc(fd);
93 char *buffer;
94 int buffersize = 256;
95 struct SignalSemaphore *sem;
97 D(bug("flock(%d, %d)\n", fd, operation));
98 if (!fdesc)
100 errno = EBADF;
101 return -1;
104 /* Sanity check */
106 (operation & ~LOCK_NB) != LOCK_SH &&
107 (operation & ~LOCK_NB) != LOCK_EX &&
108 (operation & ~LOCK_NB) != LOCK_UN
111 errno = EINVAL;
112 return -1;
115 /* Get the full path of the flocked filesystem object */
118 if(!(buffer = AllocVec(buffersize, MEMF_ANY)))
120 errno = __stdc_ioerr2errno(IoErr());
121 return -1;
124 if(NameFromFH(fdesc->fcb->handle, (STRPTR) ((IPTR) buffer + 6), buffersize - 7))
125 break;
126 else if(IoErr() != ERROR_LINE_TOO_LONG)
128 errno = __stdc_ioerr2errno(IoErr());
129 FreeVec(buffer);
130 return -1;
132 FreeVec(buffer);
133 buffersize *= 2;
135 while(TRUE);
137 CopyMem("FLOCK(", buffer, strlen("FLOCK("));
138 buffer[strlen(buffer) + 1] = '\0';
139 buffer[strlen(buffer)] = ')';
141 D(bug("[flock] Semaphore name: %s\n", buffer));
143 /* Find semaphore named FLOCK(path), add a new one if not found any */
144 Forbid();
145 sem = FindSemaphore((STRPTR) buffer);
146 if(!sem)
148 D(bug("[flock] Semaphore %s not found, creating a new one\n", buffer));
149 sem = (struct SignalSemaphore*)
150 AllocVec(sizeof(struct SignalSemaphore), MEMF_PUBLIC|MEMF_CLEAR);
151 if(!sem)
153 errno = ENOMEM;
154 Permit();
155 return -1;
157 sem->ss_Link.ln_Name = buffer;
158 AddSemaphore(sem);
160 else
162 D(bug("[flock] Semaphore %s found, freeing buffer\n", buffer));
163 FreeVec(buffer);
166 if(operation & LOCK_UN)
168 D(bug("[flock] Releasing semaphore %s\n", sem->ss_Link.ln_Name));
169 ReleaseSemaphore(sem);
170 RemoveFromList(sem);
171 if(sem->ss_Owner == NULL && sem->ss_QueueCount == -1)
173 D(bug("[flock] All locks unlocked, removing semaphore %s\n", sem->ss_Link.ln_Name));
174 /* All locks for this file were unlocked, we don't need semaphore
175 * anymore */
176 RemSemaphore(sem);
177 FreeVec(sem->ss_Link.ln_Name);
178 FreeVec(sem);
179 Permit();
180 return 0;
183 Permit();
185 switch(operation & ~LOCK_NB)
187 case LOCK_SH:
188 D(bug("[flock] Obtaining shared lock\n"));
189 if(operation & LOCK_NB)
191 if(!AttemptSemaphoreShared(sem))
193 errno = EWOULDBLOCK;
194 return -1;
197 else
198 ObtainSemaphoreShared(sem);
199 D(bug("[flock] Shared lock obtained\n"));
200 AddToList(sem);
201 break;
202 case LOCK_EX:
203 D(bug("[flock] Obtaining exclusive lock\n"));
204 if(operation & LOCK_NB)
206 if(!AttemptSemaphore(sem))
208 errno = EWOULDBLOCK;
209 return -1;
212 else
213 ObtainSemaphore(sem);
214 D(bug("[flock] Exclusive lock obtained\n"));
215 AddToList(sem);
216 break;
218 return 0;
221 LONG AddToList(struct SignalSemaphore *sem)
223 struct PosixCIntBase *PosixCBase =
224 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
225 struct FlockNode *node;
226 node = AllocMem(sizeof(struct FlockNode), MEMF_ANY | MEMF_CLEAR);
227 if(!node)
228 return -1;
229 node->sem = sem;
230 AddHead((struct List *)PosixCBase->file_locks, (struct Node*) node);
231 return 0;
234 void RemoveFromList(struct SignalSemaphore *sem)
236 struct PosixCIntBase *PosixCBase =
237 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
238 struct FlockNode *varNode;
239 struct Node *tmpNode;
241 ForeachNodeSafe(PosixCBase->file_locks, varNode, tmpNode)
243 if(varNode->sem == sem)
245 Remove((struct Node*) varNode);
246 FreeMem(varNode, sizeof(struct FlockNode));
247 break;
252 /* __init_flocks is called during library init.
253 PosixCBase->file_locks will be initialized here and then copied
254 in all libbases for each open of the library.
255 This means that a global file_locks list is used, this is needed as flocks
256 are used for locking between different processes.
258 int __init_flocks(struct PosixCIntBase *PosixCBase)
260 PosixCBase->file_locks = AllocMem(sizeof(struct MinList), MEMF_PUBLIC);
261 NEWLIST(PosixCBase->file_locks);
263 D(bug("[flock] Initialized lock list at 0x%p\n", PosixCBase->file_locks));
265 return 1;
268 /* This function is called once before root libbase would be freed.
269 This function will be called when no other program has arosc.library open
270 so no protection should be needed.
272 void __unlock_flocks(struct PosixCIntBase *PosixCBase)
274 struct FlockNode *lock;
275 struct SignalSemaphore *sem;
277 D(bug("[flock] Freeing lock list at 0x%p\n", PosixCBase->file_locks));
279 while ((lock = (struct FlockNode *) REMHEAD(PosixCBase->file_locks)))
281 sem = lock->sem;
282 ReleaseSemaphore(sem);
283 FreeMem(lock, sizeof(struct FlockNode));
285 if (sem->ss_Owner != NULL || sem->ss_QueueCount != -1)
286 bug("[flock] Freeing locked semaphore!");
288 D(bug("[flock] removing semaphore %s\n", sem->ss_Link.ln_Name));
289 RemSemaphore(sem);
290 FreeVec(sem->ss_Link.ln_Name);
291 FreeVec(sem);
293 FreeMem(PosixCBase->file_locks, sizeof(struct MinList));
294 PosixCBase->file_locks = NULL;
297 ADD2INITLIB(__init_flocks, 1);
298 ADD2EXPUNGELIB(__unlock_flocks, 1);