1 /* $Header: /home/cvs/wavplay/locks.c,v 1.2 1999/12/04 00:01:20 wwg Exp $
2  * Warren W. Gay VE3WWG		Sat May 11 15:01:58 1996
3  *
4  * MANAGE LOCKS ON THE AUDIO DEVICE:
5  *
6  * 	X LessTif WAV Play :
7  *
8  * 	Copyright (C) 1997  Warren W. Gay VE3WWG
9  *
10  * This  program is free software; you can redistribute it and/or modify it
11  * under the  terms  of  the GNU General Public License as published by the
12  * Free Software Foundation version 2 of the License.
13  *
14  * This  program  is  distributed  in  the hope that it will be useful, but
15  * WITHOUT   ANY   WARRANTY;   without   even  the   implied   warranty  of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details (see enclosed file COPYING).
18  *
19  * You  should have received a copy of the GNU General Public License along
20  * with this  program; if not, write to the Free Software Foundation, Inc.,
21  * 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * Send correspondance to:
24  *
25  * 	Warren W. Gay VE3WWG
26  *
27  * Email:
28  *	ve3wwg@yahoo.com
29  *	wgay@mackenziefinancial.com
30  *
31  * $Log: locks.c,v $
32  * Revision 1.2  1999/12/04 00:01:20  wwg
33  * Implement wavplay-1.4 release changes
34  *
35  * Revision 1.1.1.1  1999/11/21 19:50:56  wwg
36  * Import wavplay-1.3 into CVS
37  *
38  * Revision 1.1  1997/04/14 00:19:33  wwg
39  * Initial revision
40  *
41  */
42 static const char rcsid[] = "@(#)locks.c $Revision: 1.2 $";
43 
44 #include <stdio.h>
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <signal.h>
49 #include <errno.h>
50 #include <unistd.h>
51 #include <sys/types.h>
52 #include <sys/ipc.h>
53 #include <sys/sem.h>
54 #include "wavplay.h"
55 
56 static int SemUndo = SEM_UNDO;
57 static ErrFunc v_erf;				/* Error reporting function */
58 static int bTimedOut = 0;			/* True if SIGALRM is raised */
59 
60 /*
61  * SIGALRM catcher:
62  */
63 static void
catch_sigalrm(int signo)64 catch_sigalrm(int signo) {
65 	bTimedOut = 1;				/* Mark as timed out */
66 }
67 
68 /*
69  * Error reporting function for this source module:
70  */
71 static void
err(const char * format,...)72 err(const char *format,...) {
73 	va_list ap;
74 
75 	if ( v_erf == NULL )
76 		return;				/* Only report error if we have function */
77 	va_start(ap,format);
78 	v_erf(format,ap);			/* Use caller's supplied function */
79 	va_end(ap);
80 }
81 
82 /*
83  * OpenDSPLocks  opens the existing set of 2 semaphores, or creates
84  * and  initializes  the new set  if  it  does  not yet exist. This
85  * program   chooses   not   to   release  the  IPC  resource  upon
86  * termination,  since the  semphore creation and initialization is
87  * not  an atomic operation, and can be prone to failure.  However,
88  * it  the  IPC resource is removed by some other means (like ipcrm
89  * command),  this  function  call  will create a new set using the
90  * supplied IPC Key 'LockIPCKey'.
91  *
92  * NOTES:
93  *
94  * Two semphores are created in this set. Semaphore # 0 is intended
95  * to be used as a /dev/dsp 'input lock' (play lock), and semaphore
96  * # 1 is the /dev/dsp 'output lock' (record lock).  I doubt if the
97  * record lock  is very useful, but there might be situations where
98  * it is needed.
99  *
100  * Class F Reporting:
101  */
102 int
OpenDSPLocks(key_t LockIPCKey,int SemUndoFlag,ErrFunc erf)103 OpenDSPLocks(key_t LockIPCKey,int SemUndoFlag,ErrFunc erf) {
104 	int ipc;
105 	int s;
106 	int e;
107 	int safety = 3;
108 	union semun u;
109 	static ushort init_sems[2] = { 1, 1 };
110 
111 	v_erf = erf;					/* Set error reporting function */
112 
113 	/*
114 	 * Set the SEM_UNDO status :
115 	 */
116 	SemUndo = SemUndoFlag ? SEM_UNDO : 0;
117 
118 	/*
119 	 * If semaphore already exists, just use it :
120 	 */
121 	while ( (ipc = semget(LockIPCKey,2,0666)) < 0 && --safety >= 0 ) {
122 
123 		/*
124 		 * Failed to find it, try creating it :
125 		 */
126 		if ( (ipc = semget(LockIPCKey,2,IPC_CREAT|IPC_EXCL|0666)) < 0 && errno != EEXIST ) {
127 			err("Unable to create a semaphore set for key 0x%lX",
128 				sys_errlist[errno],
129 				LockIPCKey);
130 			return -1;	/* No system IPC resources? */
131 		}
132 
133 		/*
134 		 * Set already exists- timing error? Try again.
135 		 */
136 		if ( ipc < 0 ) {
137 			sleep(1);	/* Allow creator time to init sems */
138 			continue;	/* Try again */
139 		}
140 
141 		/*
142 		 * We created the semaphore set - initialize so that
143 		 * each semaphore has the value 1 :
144 		 */
145 		u.array = &init_sems[0];
146 
147 		if ( (s = semctl(ipc,0,SETALL,u)) < 0 && errno == EIDRM ) {
148 			/*
149 			 * Another process removed our ipc resource :
150 			 */
151 			continue;	/* Try again */
152 		}
153 
154 		if ( s < 0 ) {
155 			/*
156 			 * We failed to initialize!
157 			 */
158 			e = errno;			/* Save error */
159 			semctl(ipc,0,IPC_RMID,NULL);	/* Destroy bad sems */
160 			err("%s: Unable to initialize semaphore set values",sys_errlist[errno=e]);
161 			return -1;			/* Return err ind. */
162 		}
163 	}
164 
165 	return ipc;	/* Return existing, or newly initialized sems */
166 }
167 
168 /*
169  * Lock the /dev/dsp device :
170  *
171  * playrecx :
172  *	0	play lock
173  *	1	record lock
174  */
175 int
LockDSP(int ipc,int playrecx,ErrFunc erf,unsigned timeout_secs)176 LockDSP(int ipc,int playrecx,ErrFunc erf,unsigned timeout_secs) {
177 	int s;
178 	int e;
179 	static struct sembuf sops[1] = { { 0, -1, SEM_UNDO } };
180 
181 	v_erf = erf;					/* Set error reporting function */
182 
183 	sops[0].sem_num = playrecx;
184 	sops[0].sem_flg = SemUndo;
185 
186 	bTimedOut = 0;					/* Reset the timeout flag */
187 
188 	if ( timeout_secs > 0 ) {
189 		signal(SIGALRM,catch_sigalrm);		/* Prepare to catch SIGALRM */
190 		alarm(timeout_secs);			/* Start the timer */
191 	}
192 
193 	while ( (s = semop(ipc,sops,1)) < 0 && errno == EINTR )
194 		if ( bTimedOut ) {
195 			err("Timed out: locking the %s semaphore",playrecx?"Record":"Play");
196 			errno = EAGAIN;			/* Mark as timed out */
197 			s = -1;
198 			goto xit;
199 		}
200 
201 	if ( s < 0 )
202 		err("%s: Locking the %s semaphore",sys_errlist[errno],playrecx?"Record":"Play");
203 
204 	/*
205 	 * Exit this procedure:
206 	 */
207 xit:	e = errno;					/* Preserve errno for this exit */
208 	alarm(0);
209 	signal(SIGALRM,SIG_DFL);
210 	errno = e;					/* Restore errno */
211 	return s;
212 }
213 
214 /*
215  * Lock the /dev/dsp device :
216  *
217  * playrecx :
218  *	0	play lock
219  *	1	record lock
220  */
221 int
UnlockDSP(int ipc,int playrecx,ErrFunc erf)222 UnlockDSP(int ipc,int playrecx,ErrFunc erf) {
223 	int s;
224 	static struct sembuf sops[1] = { { 0, +1, SEM_UNDO } };
225 
226 	v_erf = erf;					/* Set error reporting function */
227 
228 	sops[0].sem_num = playrecx;
229 	sops[0].sem_flg = SemUndo;
230 
231 	while ( (s = semop(ipc,sops,1)) < 0 && errno == EINTR ) ;
232 
233 	if ( s < 0 )
234 		err("%s: Unlocking the %s semaphore",
235 			sys_errlist[errno],
236 			playrecx?"Record":"Play");
237 
238 	return s;
239 }
240 
241 /* $Source: /home/cvs/wavplay/locks.c,v $ */
242