1 /* Copyright (C) 2006  Britton Leo Kerin, see copyright. */
2 
3 /* This function performs the actual playing run as specified by the
4    options. */
5 
6 #include <errno.h>
7 #include <math.h>
8 #include <sched.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <signal.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <sys/mman.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 
18 #include "rawrec.h"
19 #include "thread_functions.h"
20 
21 /* This stuff is here instead of in the headers because it is grottish
22    hackery which should go as soon as kernel pthread support gets
23    better and libc uses it.  */
24 /* This flag gets set from the handler for signals we watch for to do
25    special shutdown processing (currently only SIGTERM), since we
26    can't POSIXly make the pthread calls directly from the handler and
27    need to work around some linux pthread signal handling deficiency.  */
28 extern int got_watched_for_shutdown_signal;
29 void shutdown_signal_handler(int signum);
30 /* This allows the following thread (move_au for play, move_fd for
31    record) to tell this main thread when they finish. */
32 pthread_mutex_t tell_main_follower_done_mutex = PTHREAD_MUTEX_INITIALIZER;
33 int tell_main_follower_done = 0;
34 
35 
play(parameters_stt * clp)36 void play(parameters_stt *clp) /* pneumonic: app: actual params pointer */
37 {
38   /* Audio device file descriptor.  The zero initializer is included
39      only to prevent gcc -Wall from complaining that `audio_fd' might
40      be used uninitialized in this function. */
41   int audio_fd = 0;
42   int arg_fd;            	/* Argument file fd. */
43   int bps;			/* the bits per sample for the format in use */
44   struct stat arg_file_stats;	/* argument file stats, in particular size */
45   /* Start playing at jump_bytes into the file.  Initialization
46      prevents -Wall from generating "might be used unitialized"
47      warning. */
48   double jump_bytes = 0;
49   double play_bytes;		/* number of bytes of data to play */
50   long audio_fragsz;		/* Kernel audio fragment size in bytes. */
51   int ringbuf_segs;		/* Number of senments in ringbuf. */
52   move_au_th_arg_stt au_th_arg;	/* move_au_th argument structure. */
53   move_fd_th_arg_stt fd_th_arg;	/* move_fd_th argument structure. */
54   int rtn;			/* For return values of pthread fctns. */
55   /* Maximum priority of FIFO thread.  Should always be initialized
56      elsewhere before use.  */
57   void *au_th_ret;		/* Audio thread return pointer. */
58   void *fd_th_ret;		/* File thread return pointer. */
59   sigset_t all_sigs;		/* Full set of all signals.  */
60   sigset_t tmp_mask;		/* Temporary signal set storage.  */
61   /* sigaction structure for shutdown handler */
62   struct sigaction shutdown_handler_act;
63   double sleep_time_secs;	/* Compute for nanosleep() */
64   struct timespec sleep_time;	/* For nanosleep() call.   */
65   /* The actual parameters used for the record/play run may differ slightly
66      from those given on the command line due to hardware limitations. */
67   parameters_stt actual_params;
68   /* pointer to the actual_params structure */
69   parameters_stt *app = NULL;	/* bogus initializer reasures compiler */
70 
71   /* If the user wants us to selfishly hold the audio device, grab it now. */
72   if ( clp->hold_audio_device == TRUE ) {
73     /* This function checks for incorrect semantics (incorrect syntax
74        is caught by the above switch and reported by usage()), issues
75        appropriate warnings for values the hardware can't support
76        (eg. 23477 Hz sampling rate is probably not supported, so the
77        value will need to be rounded), and on success returns the
78        actual parameters to be used for the recording or playing run
79        into actual_params.  If the user has things really fouled up,
80        execution will be terminated. */
81     actual_params = process_command_line(clp);
82     app = &actual_params;
83     audio_fd = audio_init(app->audio_dev, FOR_WRITING, app->speed,
84 			  app->format, app->channels);
85   }
86 
87   /* Pause execution as promised.  If both -p seconds and -P samples
88      are specified, the one which is longest in real time is the one
89      used. */
90   sleep_on_option(clp->time_startpause, clp->samp_startpause, clp->speed);
91 
92   /* If we arn't holding the audio device, we still have to
93      process_command_line.  This needs to be done asap, so we do it
94      here. */
95   if ( clp->hold_audio_device == FALSE ) {
96     actual_params = process_command_line(clp);
97     app = &actual_params;
98   }
99 
100   /* Determine the bits per sample from the format.  The format
101      specified by the user (and stored in the structure pointed to by
102      clp) is used, since if this differs from the driver compatible
103      format selected in actual_params, then translation is presumably
104      in use.  At present, no translation is performed, and I'm not at
105      all sure I ever want to support translating between samples of
106      different resolutions anyway. */
107   if ( (strcmp(clp->format, "s16_le") == 0)
108                || (strcmp(clp->format, "u16_le") == 0)
109                || (strcmp(clp->format, "s16_be") == 0)
110                || (strcmp(clp->format, "u16_be") == 0))
111     bps = 16;
112   else if ( (strcmp(clp->format, "s8") == 0) ||
113                (strcmp(clp->format, "u8") == 0) )
114     bps = 8;
115   else {
116     err_die("BUG: unrecognized sample format seen in function '%s'\n",
117 	    __func__);
118   }
119 
120   /* Open the argument data file to be read from, or use standard input. */
121   if ( app->using_stdio )
122     arg_fd = STDIN_FILENO;
123   else
124     arg_fd = data_init(app->arg_file, FOR_READING);
125 
126   /* If we're not using stdio... */
127   if (app->using_stdio == FALSE )
128     /* stat the argument file.  we're interested in it's size in bytes. */
129     if ( fstat(arg_fd, &arg_file_stats) == -1 ) {
130       err_die("fstat of argument file %s failed: %s\n", app->arg_file,
131 	      strerror(errno));
132     }
133 
134   /* Determine the actual amount of data to be played, not including
135      pause time or end jumping (time_endjump or samp_endjump). */
136   if ( app->timelim > (double) app->samplim / app->speed )
137     play_bytes = rint(app->timelim * app->speed) * bps
138                  * (app->channels) / 8;
139   else
140     /* Note that precedence is critical here (play_bytes_may overflow
141        without parenthesies). */
142     play_bytes = (double) app->samplim * (bps * app->channels / 8);
143 
144   /* Unless we're already selfishly holding the audio device, open it now. */
145   if ( app->hold_audio_device == FALSE )
146     audio_fd = audio_init(app->audio_dev, FOR_WRITING, app->speed,
147 			  app->format, app->channels);
148 
149   /* Set or get the kernel audio buffer block size. */
150   if ( app->set_fragsz == USER_FRAGSZ ) {
151     set_au_blksz(audio_fd, app->fragsz);
152     audio_fragsz = app->fragsz;
153   } else			/* app->set_fragsz == AUTO_FRAGSZ */
154     /* Get the block size used by the audio driver. */
155     audio_fragsz = get_au_blksz(audio_fd);
156 
157   /* Compute the number of bytes to jump into the file.  If both -j
158      seconds and -J samples are specified, the one which is longest in
159      real time (farthest into the file) in the one used. */
160   if ( app->time_startjump > 0 || app->samp_startjump > 0 ) {
161     if ( app->time_startjump > ( (double) app->samp_startjump)
162                / app->speed ) {
163       jump_bytes = floor( (double) app->time_startjump
164 			  * (double) app->speed * (double) bps
165 			  * (double) (app->channels) / 8.0);
166       /* We must be sure to jump a multiple of (app->channels * bps / 8). */
167       /* FIXME: The use of doubles in this loop conditional causes a
168 	 compiler warning, which is ok since doubles need to stop
169 	 being used for this sort of stuff.  */
170       for ( ; jump_bytes / (app->channels * bps / 8)
171 	      != floor(jump_bytes / (app->channels * bps / 8))
172 	      ; jump_bytes-- )
173 	;
174     } else
175       jump_bytes = (double) app->samp_startjump * (double) bps
176 	           * (double) (app->channels) / 8.0;
177     if ( app->using_stdio == TRUE ) {
178       unsigned char *tmp_buf;	/* Temporary buffer.  */
179       double bytes_jumped;	/* Bytes jumped so far.  */
180       long bytes_read;		/* Bytes read by last read.  */
181       int empty_seg_seq_length = 0; /* Empty reads in a row.  */
182 
183       /* For consistency with the way we treat the data we read from
184          the pipe that we actually want to play, we read in
185          app->fragsz chunks and give up in some of the same
186          situations.  */
187       if ( (tmp_buf = (unsigned char *) malloc( (size_t) audio_fragsz))
188                == NULL ) {
189 	err_die("malloc failed: %s\n", strerror(errno));
190       }
191 
192       /* Do the reads (and throw away the results).  */
193       for ( bytes_jumped = 0 ; bytes_jumped < jump_bytes ;
194                bytes_jumped += bytes_read ) {
195         if ( (bytes_read = read(arg_fd, tmp_buf, (size_t) min(audio_fragsz,
196                jump_bytes - bytes_jumped))) == -1 ) {
197 	  err_die("read from standard input failed: %s\n", strerror(errno));
198 	}
199 
200         /* Count empty reads in a row.  */
201         if ( bytes_read == 0 )
202 	  empty_seg_seq_length++;
203         else
204 	  empty_seg_seq_length = 0;
205 
206         /* Deal with case where we are getting nothing from stdin.  */
207         if ( empty_seg_seq_length >= MAGIC_EMPTY_SEG_SEQ_LENGTH ) {
208 	  /* Frailty here: there is also time_limit_set but we don't
209              use it.  It needs to go away eventually or something.  */
210           if ( (app->samp_limit_set == TRUE)
211                || (app->time_limit_set == TRUE) ) {
212 	    err_die("too many empty segments seen while skipping as per -j or -J options (starved for standard input), giving up\n");
213 	  } else { /* th_arg->limit_set == FALSE */
214 	    if ( app->verbose == TRUE ) {
215 	      err_die("warning: standard input ended without supplying enough data to satisfy the given skip option (-j or -J)\n");
216 	    }
217 	  }
218 	}
219       }
220 
221       free(tmp_buf);
222 
223     } else { /* app->using_stdio == FALSE */
224       /* Use lseek to do the actual jumping.  */
225       if ( lseek(arg_fd, (off_t) jump_bytes, SEEK_SET) == -1 ) {
226 	err_die("lseek on %s failed: %s\n", app->arg_file, strerror(errno));
227       }
228     }
229   }
230 
231   /* Set up the ring buffer, it's sub buffers, and their associated
232      mutexs. */
233   ringbuf_segs = ringbuf_init(app->ringbufsz, audio_fragsz);
234 
235   /* Fill the move_au_th argument structure. */
236   au_th_arg.startup_order = 2;
237   au_th_arg.ringbuf_segs = ringbuf_segs;
238   au_th_arg.seg_sz = audio_fragsz;
239   au_th_arg.fd = audio_fd;
240   au_th_arg.recorder = 0;
241   au_th_arg.byte_cnt = play_bytes;
242   au_th_arg.sample_size = bps * app->channels / 8;
243   if ( (app->time_limit_set) || (app->samp_limit_set) )
244     au_th_arg.limit_set = TRUE;
245   else
246     au_th_arg.limit_set = FALSE;
247   /* Fill the move_fd_th argument structure. */
248   fd_th_arg.startup_order = 1;
249   fd_th_arg.ringbuf_segs = ringbuf_segs;
250   fd_th_arg.seg_sz = audio_fragsz;
251   fd_th_arg.fd = arg_fd;
252   fd_th_arg.recorder = 0;
253   fd_th_arg.byte_cnt = play_bytes;
254   fd_th_arg.using_stdio = app->using_stdio;
255 
256   /* Set thread attributes.  See thread_scheme.txt in docs/programmer
257      for rational. */
258   if ( (rtn = pthread_attr_init(&move_au_attr)) ) {
259     err_die("BUG: pthread_attr_init failed: %s\n", strerror(rtn));
260   }
261   if ( (rtn = pthread_attr_setdetachstate(&move_au_attr,
262 					  PTHREAD_CREATE_JOINABLE)) ) {
263     err_die("BUG: pthread_attr_setdetachstate failed: %s\n", strerror(rtn));
264   }
265 
266 /* This is ugly, but FreeBSD defines _POSIX_THREAD_PRIORITY_SCHEDULING
267    but it does not support PTHREAD_SCOPE_SYSTEM
268 
269 #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) \
270     && _POSIX_THREAD_PRIORITY_SCHEDULING != -1 \
271     && _POSIX_THREAD_PRIORITY_SCHEDULING != 0
272   if ( have_root_authority ) {
273     if ( (rtn = pthread_attr_setinheritsched(&move_au_attr,
274 					     PTHREAD_EXPLICIT_SCHED)) ) {
275       err_die("BUG: pthread_attr_setinheritsched failed: %s\n", strerror(rtn));
276     }
277     if ( (rtn = pthread_attr_setschedpolicy(&move_au_attr, SCHED_FIFO)) )
278       err_die("BUG: pthread_attr_setschedpolicy failed: %s\n", strerror(rtn));
279     if ( (fifo_max_prio = sched_get_priority_max(SCHED_FIFO)) == -1 )
280       err_die("BUG: sched_get_priority_max failed: %s", strerror(errno));
281     move_au_param.sched_priority = fifo_max_prio;
282     if ( (rtn = pthread_attr_setschedparam(&move_au_attr, &move_au_param)) )
283       err_die("BUG: pthread_attr_setschedparam failed: %s\n", strerror(rtn));
284     if ( (rtn = pthread_attr_setscope(&move_au_attr, PTHREAD_SCOPE_SYSTEM)) )
285       err_die("BUG: pthread_attr_setscope failed: %s\n", strerror(rtn));
286   }
287 #endif
288 */
289 
290   if ( (rtn = pthread_attr_init(&move_fd_attr)) )
291     err_die("BUG: pthread_attr_init failed: %s\n", strerror(rtn));
292   if ( (rtn = pthread_attr_setdetachstate(&move_fd_attr,
293 					  PTHREAD_CREATE_JOINABLE)) ) {
294     err_die("BUG: pthread_attr_setdetachstate failed: %s\n", strerror(rtn));
295   }
296 
297 /* This is ugly, but FreeBSD defines _POSIX_THREAD_PRIORITY_SCHEDULING
298    but it does not support PTHREAD_SCOPE_SYSTEM
299 
300 #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) \
301     && _POSIX_THREAD_PRIORITY_SCHEDULING != -1 \
302     && _POSIX_THREAD_PRIORITY_SCHEDULING != 0
303   if ( have_root_authority ) {
304     if ( (rtn = pthread_attr_setinheritsched(&move_fd_attr,
305 					     PTHREAD_EXPLICIT_SCHED)) ) {
306       err_die("BUG: pthread_attr_setinheritsched failed: %s\n", strerror(rtn));
307     }
308     if ( (rtn = pthread_attr_setschedpolicy(&move_fd_attr, SCHED_FIFO)) )
309       err_die("BUG: pthread_attr_setschedpolicy failed: %s\n", strerror(rtn));
310     move_fd_param.sched_priority = fifo_max_prio;
311     if ( (rtn = pthread_attr_setschedparam(&move_fd_attr, &move_fd_param)) )
312       err_die("BUG: pthread_attr_setschedparam failed: %s\n", strerror(rtn));
313     if ( (rtn = pthread_attr_setscope(&move_fd_attr, PTHREAD_SCOPE_SYSTEM)) )
314       err_die("BUG: pthread_attr_setscope failed: %s\n", strerror(rtn));
315   }
316 #endif
317 */
318   /* Getting ugly.  Here we install a handler (which sets a global
319      flag which the threads can poll in order to do graceful
320      death). */
321   shutdown_handler_act.sa_handler = shutdown_signal_handler;
322   sigfillset(&(shutdown_handler_act.sa_mask));
323   shutdown_handler_act.sa_flags = 0;
324   if ( sigaction(SIGTERM, &shutdown_handler_act, NULL) == -1 )
325     err_die("sigaction failed: %s\n", strerror(errno));
326   if ( sigaction(SIGINT, &shutdown_handler_act, NULL) == -1 )
327     err_die("sigaction failed: %s\n", strerror(errno));
328 
329   /* Set up an empty signal set.  */
330   if ( sigfillset(&all_sigs) == -1 )
331     err_die("sigfillset failed: %s\n", strerror(errno));
332   /* POSIX requires all signal sets to be initialized before use.  */
333   if ( sigemptyset(&tmp_mask) == -1 )
334     err_die("sigemptyset failed: %s\n", strerror(errno));
335   /* Block all signals in preperation for starting threads.  We are
336      moving into some linux specific hackery now, though it shouldn't
337      make problems elsewhere.  The threads never unblock the signals,
338      so the processes they run in are effectively unsignalable to
339      everything but the unblockable signals.  Once the threads are
340      started, the main thread handles important signals (at the
341      moment, SIGTERM) and nanosleeps.  On sight of an important
342      signal, we set a locked flag indicating that the signal has
343      occured.  The move_au and move_fd threads poll this flag as
344      readers, and exit at the end of the segment in progress if they
345      detect it.  The "segment in progress" is the one which move_au is
346      acting on, i.e. when playing, we try to exit as soon as possible,
347      we don't try to finish playing all buffered data move_fd may have
348      loaded for use.  Note that large segment sizes may still result
349      in significant signal response latency.  */
350   if ( sigprocmask(SIG_BLOCK, &all_sigs, &tmp_mask) == -1 )
351     err_die("sigprocmask failed: %s\n", strerror(errno));
352 
353   if ( have_root_authority ) {
354     /* Get root authority (the saved set-user id should be root).  */
355     if ( seteuid( (uid_t) 0 ) == -1 )
356       err_die("seteuid( (uid_t) 0 ) failed: %s\n", strerror(errno));
357 
358     /* Entering critical section.  Lock down our memory, if possible.  */
359 #if defined (_POSIX_MEMLOCK) && _POSIX_MEMLOCK != -1 && _POSIX_MEMLOCK != 0
360     if ( mlockall(MCL_CURRENT) == -1 )
361       err_die("mlockall(MCL_CURRENT) failed: %s\n", strerror(errno));
362 #endif
363   }
364 
365   /* Start threads.  If we have root authority, then these threads
366      will be using real time scheduling, and we must therefore be root
367      to start them, and they therefore inherit effective root ids, but
368      drop them as soon as all threads have been created.. */
369   if ( (rtn = pthread_create (&move_au_th, &move_au_attr,
370 			      (void *(*)(void *)) move_au, &au_th_arg)) ) {
371     err_die ("BUG: pthread_create failed to create audio thread: %s\n",
372 	     strerror (rtn));
373   }
374   if ( (rtn = pthread_create (&move_fd_th, &move_fd_attr,
375 			      (void *(*)(void *)) move_fd, &fd_th_arg)) ) {
376     err_die ("BUG: pthread_create failed to create file thread: %s\n",
377 	     strerror (rtn));
378   }
379 
380   if ( have_root_authority ) {
381     /* Drop back to normal uid authority. */
382     if ( seteuid(getuid()) == -1 )
383       /* If for some crazy reason we fail to drop root permissions, exit
384 	 immediately without advertising the fact. */
385       exit (EXIT_FAILURE);
386     /* Let other threads know that we have dropped root permissions
387        (they wait for this before doing anything).  */
388     pthread_mutex_lock(&root_permissions_dropped_mutex);
389     root_permissions_dropped = 1;
390     pthread_cond_broadcast(&root_permissions_dropped_cv);
391     pthread_mutex_unlock(&root_permissions_dropped_mutex);
392   }
393 
394   /* Restore the default mask.  Note that at no time has the default
395      action for the shutdown signals we handle (sloppy death in which
396      this initial thread expires but leaves its spawned threads to
397      thrash on for a brief period, hopefully this mess will be fixed
398      with kernel 2.5 and glibc 2.3) been allowed to take place after
399      threads have been spawned.  */
400   if ( sigprocmask(SIG_SETMASK, &tmp_mask, NULL) == -1 )
401     err_die("sigprocmask failed: %s\n", strerror(errno));
402 
403   /* Now here is some sad hackery.  We can't sigwait() because these
404      are asynchronous signals we are worried about, and they might
405      never arrive, which would cause our main thread to hang forever.
406      We can't create a dedicated thread and deflect signals there,
407      because Linux pthreads aren't POSIX-conformant in that respect.
408      So, we nanosleep in a loop (note that if we don't have a time or
409      sample limit set, then the sample limit will have been set to its
410      maximum value in process_comand_line (needs cleanup) and wait for
411      a signal to come in and fire a handler for the only signal we
412      currently deal with, SIGTERM.  The SIGTERM handler sets a global
413      flag indicating that we got a shutdown signal, and we pass the
414      word on to the thread functions via a mutex protected flag, then
415      break out of the loop.  Also, the move_au thread packs it in when
416      it doesn't find any data to read or finishes normally.  It has to
417      let this main thread know when this happens so we can stop
418      sleeping and waiting for signals and commence pthread_joining.
419      It tells us via mutex-protected tell_main_follower_done, which we
420      poll from here every half audio fragsz worth of time (hopefully
421      giving maximum latency < fragsz as advertized).  */
422   sleep_time_secs = (8.0 * audio_fragsz) / (2 * bps
423 					    * app->speed * app->channels);
424   sleep_time.tv_sec = (time_t) floor(sleep_time_secs);
425   sleep_time.tv_nsec = (long) ((sleep_time_secs - floor(sleep_time_secs))
426 			       * 1000000000.0);
427   for ( ; ; ) {
428     nanosleep(&sleep_time, NULL);
429 
430     /* Has the following thread (move_au in this case, since we are
431        playing) finished? */
432     pthread_mutex_lock(&tell_main_follower_done_mutex);
433     if ( tell_main_follower_done ) {
434       pthread_mutex_unlock(&tell_main_follower_done_mutex);
435       break;
436     }
437     pthread_mutex_unlock(&tell_main_follower_done_mutex);
438 
439     /* We will be good boys and block watched for signals before
440        checking whether we have seen them or not, even though branch on
441        an int should be pretty darn atomic, and if we have made it this
442        far without getting the signal we no longer care much whether we
443        get it or not.  */
444     if ( sigemptyset(&tmp_mask) == -1 )
445       err_die("sigemptyset failed: %s\n", strerror(errno));
446     if ( sigaddset(&tmp_mask, SIGTERM) == -1 )
447       err_die("sigaddset failed: %s\n", strerror(errno));
448     if ( sigprocmask(SIG_BLOCK, &tmp_mask, NULL) == -1 )
449       err_die("sigprocmask failed: %s\n", strerror(errno));
450     if ( got_watched_for_shutdown_signal ) {
451       /* Let the move_au and move_fd threads know that we have been
452 	 interrupted by a signal that we catch in order to do clean
453 	 program termination, then break out of nanosleep loop.
454 	 Threads check shutdown_signal_seen after reading or writing
455 	 each segment from the ring buffer.  This could be done
456 	 slightly more efficiently with a reader-writer lock for the
457 	 play case, but it probably isn't worth the hassle and
458 	 complexity.  */
459       pthread_mutex_lock(&shutdown_signal_seen_mutex);
460       shutdown_signal_seen = 1;
461       pthread_mutex_unlock(&shutdown_signal_seen_mutex);
462       if ( sigprocmask(SIG_UNBLOCK, &tmp_mask, NULL) == -1 )
463 	err_die("sigprocmask failed: %s\n", strerror(errno));
464       break;
465     }
466     if ( sigprocmask(SIG_UNBLOCK, &tmp_mask, NULL) == -1 )
467       err_die("sigprocmask failed: %s\n", strerror(errno));
468   }
469 
470   /* Wait for threads to finish. */
471   if ( (rtn = pthread_join(move_au_th, &au_th_ret)) )
472     err_die("BUG: pthread_join failed: %s\n", strerror(rtn));
473   if ( *( (int *) au_th_ret) == -1 )
474     err_die("abnormal termination of move_au_th, aborting\n");
475   if ( (rtn = pthread_join(move_fd_th, &fd_th_ret)) )
476     err_die("BUG: pthread_join failed: %s\n", strerror(rtn));
477   if ( *( (int *) fd_th_ret) == -1 )
478     err_die("abnormal termination of move_fd_th, aborting\n");
479 
480   /* As per our promise not to do things like end record (-e or -E) or
481      end pause (-z or -Z) if we got interrupted by a signal.  This
482      behavior may change eventually, but for the moment, since we only
483      do special processing on SIGTERM, and the other terminating
484      signals cause the clunky natural death which doesn't honor these
485      options, we do it this way for consistency.  */
486   if ( got_watched_for_shutdown_signal ) {
487     ringbuf_close();  /* Free ring buffer and paraphenalia.  */
488     data_close(arg_fd, app->arg_file);  /* Close the argument data file.  */
489     audio_close(audio_fd, app->audio_dev);
490     return;
491   }
492 
493   /* Unlock our address space.  Requires root permissions, perhaps best
494      not to bother. */
495     /* if ( munlockall() == -1 ) {
496      *   fprintf(stderr, "%s: munlockall() failed: ", progname);
497      *   perror("");
498      *   exit(EXIT_FAILURE);
499      * }
500      */
501 
502   ringbuf_close();  /* Free ring buffer and paraphenalia. */
503 
504   data_close(arg_fd, app->arg_file);      /* Close the argument data file. */
505 
506   /* Unless we are selfishly holding onto the audio device... */
507   if ( app->hold_audio_device == FALSE )
508     audio_close(audio_fd, app->audio_dev);
509 
510   /* Pause at end as promised. */
511   sleep_on_option(app->time_endpause, app->samp_endpause, app->speed);
512 
513   /* If we have been selfishly holding onto the audio device, let it go now. */
514   if ( app->hold_audio_device == TRUE )
515     audio_close(audio_fd, app->audio_dev);
516 
517   return;
518 }
519