1 /*
2 ** Vsound - a virtual audio loopback cable used for recording /dev/dsp streams
3 ** Copyright (C) 2004 Nathan Chantrell <nsc@zorg.org>
4 ** Copyright (C) 2003 Richard Taylor <r.taylor@bcs.org.uk>
5 ** Copyright (C) 2000,2001 Erik de Castro Lopo <erikd@zip.com.au>
6 ** Copyright (C) 1999 James Henstridge <james@daa.com.au>
7 **
8 ** Based on the esddsp utility that is part of esound. Esddsp's copyright:
9 ** Copyright (C) 1998, 1999 Manish Singh <yosh@gimp.org>
10 **
11 ** This library is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU Library General Public
13 ** License as published by the Free Software Foundation; either
14 ** version 2 of the License, or (at your option) any later version.
15 **
16 ** This library is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** Library General Public License for more details.
20 **
21 ** You should have received a copy of the GNU Library General Public
22 ** License along with this library; if not, write to the
23 ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ** Boston, MA 02111-1307, USA.
25 */
26
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/stat.h>
35 #include <stdio.h>
36 #include <signal.h>
37
38 #ifdef ENABLE_DEBUG
39 #include <errno.h>
40 #define DPRINTF(format, args...) fprintf(stderr, format, ## args)
41 #else
42 #define DPRINTF(format, args...)
43 #endif
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #ifdef HAVE_MACHINE_SOUNDCARD_H
50 #include <machine/soundcard.h>
51 #else
52 #ifdef HAVE_SOUNDCARD_H
53 #include <soundcard.h>
54 #else
55 #include <sys/soundcard.h>
56 #endif
57 #endif
58
59 /* It seems that Debian Woody (and possibly others) do not define RTLD_NEXT. */
60 #include <dlfcn.h>
61 #ifndef RTLD_NEXT
62 #define RTLD_NEXT ((void *) -1l)
63 #endif
64
65 #define REAL_LIBC RTLD_NEXT
66
67 #ifdef __FreeBSD__
68 typedef unsigned long request_t;
69 #else
70 typedef int request_t;
71 #endif
72
73 /*------------------------------------------------------------------------------
74 ** Macros to handle big/little endian issues.
75 */
76 #ifdef HAVE_ENDIAN_H
77 /* This is the best way to do it. Unfortunately Sparc Solaris (and
78 ** possibly others) don't have <endian.h>
79 */
80 #include <sys/endian.h>
81 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
82 #define CPU_IS_LITTLE_ENDIAN 1
83 #define CPU_IS_BIG_ENDIAN 0
84 #elif (__BYTE_ORDER == __BIG_ENDIAN)
85 #define CPU_IS_LITTLE_ENDIAN 0
86 #define CPU_IS_BIG_ENDIAN 1
87 #else
88 #error "A bit confused about endian-ness! Have <endian.h> but not __BYTEORDER."
89 #endif
90 #else
91 /* If we don't have <endian.h> use the guess based on target processor
92 ** from the autoconf process.
93 */
94 #if GUESS_LITTLE_ENDIAN
95 #define CPU_IS_LITTLE_ENDIAN 1
96 #define CPU_IS_BIG_ENDIAN 0
97 #elif GUESS_BIG_ENDIAN
98 #define CPU_IS_LITTLE_ENDIAN 0
99 #define CPU_IS_BIG_ENDIAN 1
100 #else
101 #error "Endian guess seems incorrect."
102 #endif
103 #endif
104
105 #define ENDSWAP_SHORT(x) ((((x)>>8)&0xFF)|(((x)&0xFF)<<8))
106 #define ENDSWAP_INT(x) ((((x)>>24)&0xFF)|(((x)>>8)&0xFF00)|(((x)&0xFF00)<<8)|(((x)&0xFF)<<24))
107
108 #if (CPU_IS_LITTLE_ENDIAN == 1)
109 #define MAKE_MARKER(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
110
111 #define H2LE_SHORT(x) (x)
112 #define H2LE_INT(x) (x)
113 #define H2BE_SHORT(x) ENDSWAP_SHORT(x)
114 #define H2BE_INT(x) ENDSWAP_INT(x)
115
116 #elif (CPU_IS_BIG_ENDIAN == 1)
117 #define MAKE_MARKER(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d))
118
119 #define H2LE_SHORT(x) ENDSWAP_SHORT(x)
120 #define H2LE_INT(x) ENDSWAP_INT(x)
121 #define H2BE_SHORT(x) (x)
122 #define H2BE_INT(x) (x)
123
124 #else
125 #error "Cannot determine endian-ness of processor."
126 #endif
127
128 #define DOTSND_MARKER (MAKE_MARKER ('.', 's', 'n', 'd'))
129 #define DNSDOT_MARKER (MAKE_MARKER ('d', 'n', 's', '.'))
130
131
132
133
134 /*----------------------------------------------------------------------------
135 ** Typedefs and enums.
136 */
137
138 typedef struct
139 { int magic ;
140 int dataoffset ;
141 int datasize ;
142 int encoding ;
143 int samplerate ;
144 int channels ;
145 } AU_HEADER ;
146
147 enum
148 { AU_ENCODING_ULAW_8 = 1, /* 8-bit u-law samples */
149 AU_ENCODING_PCM_8 = 2, /* 8-bit linear samples */
150 AU_ENCODING_PCM_16 = 3, /* 16-bit linear samples */
151 AU_ENCODING_ALAW_8 = 27, /* 8-bit A-law samples */
152 } ;
153
154 /*----------------------------------------------------------------------------
155 ** Function Prototypes.
156 */
157
158 static void dsp_init (void) ;
159 static int dspctl (request_t request, void *argp) ;
160 static void fix_header (AU_HEADER *au_header) ;
161 static void endswap_short_array (short* buffer, int count) ;
162 static int au_bytes_per_sample (AU_HEADER *au_header) ;
163 static u_long usec_diff_timeval (struct timeval *start, struct timeval *end) ;
164 static void start_autostop_timer(void);
165 static void stop_autostop_timer(void);
166
167 /*----------------------------------------------------------------------------
168 ** Static Data.
169 */
170
171 static int filefd = -1, dspfd = -1 ;
172 static int enable_dspout = 0, enable_stdout = 0, enable_timing = 0 ;
173 static int devdsp_format = 0, done_header = 0 ;
174 static int stopdelay = 0, ignore_autostop = 1;
175
176 static char *datafile = NULL ;
177
178 static AU_HEADER au_header =
179 { DNSDOT_MARKER, /* magic */
180 sizeof (AU_HEADER), /* dataoffset */
181 0xFFFFFFFF, /* datasize - unknown length. */
182 AU_ENCODING_PCM_16, /* encoding */
183 44100, /* samplerate */
184 2 /* channels */
185 } ;
186
187 static struct {
188 u_long max_samples ;
189 struct timeval start_time ;
190 struct timeval current_time ;
191 u_long bytes_per_sample ;
192 u_long samples_written ;
193 u_long sample_rate ;
194 } virtual_device ;
195
196 /*============================================================================
197 ** Public Functions.
198 */
199
open(const char * pathname,int flags,...)200 int open (const char *pathname, int flags, ...)
201 { static int (*func_open) (const char *, int, mode_t) = NULL;
202 va_list args;
203 mode_t mode;
204 int fd ;
205
206
207 if (!func_open)
208 func_open = (int (*) (const char *, int, mode_t)) dlsym (REAL_LIBC, "open") ;
209
210 dsp_init () ;
211
212 va_start (args, flags) ;
213 mode = va_arg (args, int) ;
214 va_end (args) ;
215
216 if (strcmp (pathname, "/dev/dsp"))
217 { fd = func_open (pathname, flags, mode) ;
218 /*DPRINTF ("Not /dev/dsp. open (%s) = %d\n", pathname, fd) ;*/
219 return fd ;
220 } ;
221
222
223 stop_autostop_timer();
224
225 /* Opening /dev/dsp device so reset done_header. */
226 done_header = 0 ;
227 au_header.datasize = 0xFFFFFFFF ;
228 /* Set time to zero. */
229 memset (&virtual_device, 0, sizeof (virtual_device)) ;
230
231 if (enable_dspout && enable_stdout)
232 { filefd = 1 ;
233 dspfd = func_open (pathname, flags, mode) ;
234 }
235 else if (enable_dspout && ! enable_stdout)
236 { filefd = func_open (datafile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR) ;
237 dspfd = func_open (pathname, flags, mode) ;
238 }
239 else if (! enable_stdout)
240 filefd = dspfd = func_open (datafile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR) ;
241 else
242 filefd = dspfd = 1 ;
243
244 DPRINTF ("open (%s) = (filefd: %d, dspfd = %d)\n", pathname, filefd, dspfd) ;
245
246 return dspfd ;
247 } /* open */
248
ioctl(int fd,request_t request,...)249 int ioctl (int fd, request_t request, ...)
250 { static int (*func_ioctl) (int, request_t, void *) = NULL;
251 va_list args;
252 void *argp;
253
254 if (! func_ioctl)
255 func_ioctl = (int (*) (int, request_t, void *)) dlsym (REAL_LIBC, "ioctl") ;
256 va_start (args, request) ;
257 argp = va_arg (args, void *) ;
258 va_end (args) ;
259
260 if (fd != dspfd)
261 return func_ioctl (fd, request, argp) ;
262
263 /* Capture the ioctl() calls to /dev/dsp. */
264 dspctl (request, argp) ;
265
266 if (enable_dspout) /* Call the real ioctl() on the/dev/dsp descriptor. */
267 return func_ioctl (dspfd, request, argp) ;
268
269 /* The RealPlayer calls this ioctl() and expects sensible results
270 ** back. So, feed it some bullshit it will believe :-).
271 */
272 if (request == SNDCTL_DSP_GETOSPACE)
273 { audio_buf_info *info ;
274
275 info = (audio_buf_info*) argp ;
276 info->fragments = 32 ;
277 info->fragstotal = 32 ;
278 info->fragsize = 4096 ;
279 info->bytes = 131072 ;
280 } ;
281
282 return 0 ;
283 } /* ioctl */
284
close(int fd)285 int close (int fd)
286 { static int (*func_close) (int) = NULL;
287 int retval = 0 ;
288
289
290 DPRINTF ("close (%d)\n", fd) ;
291
292 if (! func_close)
293 func_close = (int (*) (int)) dlsym (REAL_LIBC, "close") ;
294
295 /* If data is going to stdout and the header hasn't been
296 ** written yet, don't close the descriptor.
297 */
298 if (fd == 1 && filefd == 1 && ! done_header)
299 return 0 ;
300
301 retval = func_close (fd) ;
302
303 if (fd == dspfd)
304 { dspfd = -1;
305 filefd = -1;
306 /* Closing /dev/dsp so reset done_header. */
307 done_header = 0 ;
308 au_header.datasize = 0xFFFFFFFF ;
309 start_autostop_timer();
310 } ;
311
312
313 return retval ;
314 } /* close */
315
write(int fd,const void * buf,size_t count)316 ssize_t write (int fd, const void *buf, size_t count)
317 { static ssize_t (*func_write) (int, const void*, size_t) = NULL ;
318 ssize_t retval = 0 ;
319
320
321 if (! func_write)
322 func_write = (ssize_t (*) (int, const void*, size_t)) dlsym (REAL_LIBC, "write") ;
323
324 if (fd != dspfd)
325 return func_write (fd, buf, count) ;
326
327 if (! done_header)
328 { fix_header (&au_header) ; /* Byte swap header if required. */
329 if (enable_dspout)
330 { DPRINTF ("Writing AU header to fd = %d.\n", filefd) ;
331 func_write (filefd, &au_header, sizeof (au_header)) ;
332 }
333 else
334 { DPRINTF ("Writing AU header to fd = %d.\n", dspfd) ;
335 func_write (dspfd, &au_header, sizeof (au_header)) ;
336 } ;
337 fix_header (&au_header) ; /* Reverse effects of earlier byte swap. */
338 done_header = 1 ;
339 }
340
341 if (! virtual_device.start_time.tv_sec)
342 { gettimeofday (& (virtual_device.start_time), NULL) ;
343 virtual_device.bytes_per_sample = au_bytes_per_sample (&au_header) ;
344 virtual_device.sample_rate = au_header.samplerate ;
345
346 /* Number of bytes was stored previously in an ioctl () call. */
347 virtual_device.max_samples /= virtual_device.bytes_per_sample ;
348 } ;
349
350 if (enable_dspout)
351 func_write (dspfd, buf, count) ;
352
353 if (CPU_IS_LITTLE_ENDIAN)
354 endswap_short_array ((short*) buf, count / sizeof (short)) ;
355
356 retval = func_write (filefd, buf, count) ;
357
358 if (retval != count)
359 DPRINTF ("write (%d) returns %d\n", filefd, retval) ;
360
361 /* Count bytes written to the file so that timing can be calculated. */
362 virtual_device.samples_written += retval / virtual_device.bytes_per_sample ;
363
364 gettimeofday (& (virtual_device.current_time), NULL) ;
365
366 if (enable_timing)
367 { u_long diff_time = usec_diff_timeval (&virtual_device.start_time, &virtual_device.current_time) ;
368 u_long usec_sleep = (1000000.0 * virtual_device.samples_written) / virtual_device.sample_rate - diff_time ;
369
370
371
372 DPRINTF ("time = %lu\n", diff_time / 1000000) ;
373
374 if (usec_sleep > 0 && usec_sleep < 1000000)
375 usleep (usec_sleep) ;
376 } ;
377
378
379 return retval ;
380 } /* write */
381
382 /*------------------------------------------------------------------------------
383 ** Static functions.
384 */
385
386 static
fix_header(AU_HEADER * pheader)387 void fix_header (AU_HEADER *pheader)
388 { if (CPU_IS_LITTLE_ENDIAN)
389 { pheader->magic = DOTSND_MARKER ;
390 pheader->dataoffset = H2BE_INT (sizeof (AU_HEADER)) ;
391 pheader->datasize = 0xFFFFFFFF ; /* Unknown length. */
392 pheader->encoding = H2BE_INT (pheader->encoding) ;
393 pheader->samplerate = H2BE_INT (pheader->samplerate) ;
394 pheader->channels = H2BE_INT (pheader->channels) ;
395 } ;
396 return ;
397 } /* fix_header */
398
399 static
endswap_short_array(short * buffer,int count)400 void endswap_short_array (short* buffer, int count)
401 { int k ;
402
403 for (k = 0 ; k < count ; k++)
404 buffer [k] = ENDSWAP_SHORT (buffer [k]) ;
405
406 } /* endswap_short_array */
407
408 static
dsp_init(void)409 void dsp_init (void)
410 { static int inited = 0;
411 char *cptr ;
412
413 if (inited) return;
414 inited = 1;
415
416 datafile = getenv ("VSOUND_DATA") ;
417 if (! datafile)
418 datafile = "./vsound.data";
419
420 cptr = getenv ("VSOUND_DSPOUT") ;
421 if (cptr)
422 { DPRINTF ("Enabling /dev/dsp output.\n") ;
423 enable_dspout = 1 ;
424 }
425 else
426 DPRINTF ("No /dev/dsp output.\n") ;
427
428 cptr = getenv ("VSOUND_TIMING") ;
429 if (cptr && ! enable_dspout)
430 { DPRINTF ("Enabling timing delays for streaming data.\n") ;
431 enable_timing = 1 ;
432 }
433 else
434 DPRINTF ("No timing delays.\n") ;
435
436 cptr = getenv ("VSOUND_STDOUT") ;
437 if (cptr)
438 { DPRINTF ("Enabling output to stdout.\n") ;
439 enable_stdout = 1 ;
440 }
441 else
442 DPRINTF ("Output goes to file.\n") ;
443
444 cptr = getenv("VSOUND_STOPDELAY");
445 if (cptr)
446 stopdelay = atoi(cptr);
447 DPRINTF("Autostoping after %d seconds of inactivity\n", stopdelay);
448
449
450
451 } /* dsp_init */
452
453 static
dspctl(request_t request,void * argp)454 int dspctl (request_t request, void *argp)
455 { int *arg = (int *) argp;
456
457 switch (request)
458 { case SNDCTL_DSP_RESET:
459 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_RESET, %p)\n", argp) ;
460 break ;
461
462 case SNDCTL_DSP_POST:
463 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_POST, %p)\n", argp) ;
464 break ;
465
466 case SNDCTL_DSP_SETFMT:
467 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_SETFMT, 0x%08X)\n", *((int*) argp)) ;
468
469 au_header.magic = CPU_IS_BIG_ENDIAN ? DOTSND_MARKER : DNSDOT_MARKER ;
470
471 switch (*arg)
472 { case AFMT_QUERY:
473 if (CPU_IS_BIG_ENDIAN)
474 *arg = AFMT_S16_BE; /* this is arbitrary */
475 else
476 *arg = AFMT_S16_LE; /* this is arbitrary */
477
478 DPRINTF ("AFMT_QUERY\n") ;
479 au_header.encoding = AU_ENCODING_PCM_16 ;
480 break ;
481
482 case AFMT_MU_LAW:
483 DPRINTF ("AFMT_MU_LAW\n") ;
484 au_header.encoding = AU_ENCODING_ULAW_8 ;
485 break ;
486
487 case AFMT_A_LAW:
488 DPRINTF ("AFMT_A_LAW\n") ;
489 au_header.encoding = AU_ENCODING_ALAW_8 ;
490 break ;
491
492 case AFMT_S8:
493 DPRINTF ("AFMT_S8\n") ;
494 au_header.encoding = AU_ENCODING_PCM_8 ;
495 break ;
496
497 case AFMT_U8:
498 DPRINTF ("AFMT_U8\n") ;
499 au_header.encoding = 0 ;
500 break ;
501
502 case AFMT_S16_LE:
503 DPRINTF ("AFMT_S16_LE\n") ;
504 au_header.magic = DNSDOT_MARKER ;
505 au_header.encoding = AU_ENCODING_PCM_16 ;
506 break ;
507
508 case AFMT_S16_BE:
509 DPRINTF ("AFMT_S16_BE\n") ;
510 au_header.magic = DOTSND_MARKER ;
511 au_header.encoding = AU_ENCODING_PCM_16 ;
512 break ;
513
514 case AFMT_U16_LE:
515 DPRINTF ("AFMT_U16_LE\n") ;
516 au_header.magic = DNSDOT_MARKER ;
517 au_header.encoding = 0 ;
518 break ;
519
520 case AFMT_U16_BE:
521 DPRINTF ("AFMT_U16_BE\n") ;
522 au_header.magic = DOTSND_MARKER ;
523 au_header.encoding = 0 ;
524 break ;
525 default:
526 DPRINTF ("Unknown format : %08X\n", *((int*) arg)) ;
527 break ;
528
529 }
530 devdsp_format = *((int*) arg) ;
531 break ;
532
533 case SNDCTL_DSP_SPEED:
534 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_SPEED, %d)\n", *((int*) argp)) ;
535 au_header.samplerate = *((int*) argp) ;
536 break ;
537
538 case SNDCTL_DSP_STEREO:
539 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_STEREO, %p)\n", argp) ;
540 au_header.channels = (*arg) ? 2 : 1 ;
541 break ;
542
543 case SNDCTL_DSP_CHANNELS:
544 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_CHANNELS, %d)\n", *((int*) argp)) ;
545 au_header.channels = *((int *) arg) ;
546 break ;
547
548 case SNDCTL_DSP_GETBLKSIZE:
549 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_GETBLKSIZE, %p)\n", argp) ;
550 /*
551 ** DPRINTF ("dsp_getblksize (returning 4k)\n") ;
552 ** *arg = 4 * 1024;
553 */
554 break ;
555
556 case SNDCTL_DSP_GETFMTS:
557 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_GETFMTS, %p)\n", argp) ;
558 /*
559 ** DPRINTF ("dsp_getfmts (returning 0x38)\n") ;
560 ** *arg = AFMT_MU_LAW|AFMT_A_LAW|AFMT_U8|AFMT_S16_LE|AFMT_S16_BE|
561 ** AFMT_S8|AFMT_U16_LE|AFMT_U16_BE;
562 */
563
564 *arg = AFMT_S16_LE | AFMT_S16_BE ;
565 DPRINTF ("dsp_getfmts (returning %d)\n", *((int*) arg)) ;
566 break ;
567
568 case SNDCTL_DSP_GETCAPS:
569 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_GETCAPS, %p)\n", argp) ;
570 /*
571 ** *arg = 0;
572 */
573 break ;
574
575 case SNDCTL_DSP_GETOSPACE:
576 { int value ;
577
578 /*
579 ** audio_buf_info *info ;
580 ** info = (audio_buf_info*) argp ;
581 **
582 ** DPRINTF (" fragments : %d\n", info->fragments) ;
583 ** DPRINTF (" fragstotal : %d\n", info->fragstotal) ;
584 ** DPRINTF (" fragsize : %d\n", info->fragsize) ;
585 ** DPRINTF (" bytes : %d\n", info->bytes) ;
586 **
587 ** audio_buf_info *bufinfo = (audio_buf_info *) argp;
588 ** bufinfo->bytes = 4 * 1024;
589 **
590 */
591
592 value = *((int*) argp) ;
593 value = value < 0 ? -value : value ;
594 DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_GETOSPACE, %p) = %d (%08X)\n", argp, value, value) ;
595 virtual_device.max_samples = value ;
596 }
597 break ;
598
599
600 default:
601 /* DPRINTF ("Unknown ioctl() : %08X\n", request) ; */
602 break ;
603 } ;
604
605 return 0;
606 } /* dspctl */
607
608 static
au_bytes_per_sample(AU_HEADER * au_header)609 int au_bytes_per_sample (AU_HEADER *au_header)
610 { int bytes = au_header->channels ;
611
612 if (au_header->encoding == AU_ENCODING_PCM_16)
613 bytes *= 2 ;
614
615 DPRINTF ("au_bytes_per_sample : %d\n", bytes) ;
616
617 return bytes ;
618 } /* au_bytes_per_sample */
619
620 static
usec_diff_timeval(struct timeval * start,struct timeval * end)621 u_long usec_diff_timeval (struct timeval *start, struct timeval *end)
622 { u_long diff ;
623
624 if (end->tv_sec < start->tv_sec)
625 { DPRINTF ("warning : end->tv_sec < start->tv_sec. Possible wrap around???\n") ;
626 return 0 ; /* Just to be safe. */
627 } ;
628
629 diff = (end->tv_sec - start->tv_sec) * 1000000 ;
630 diff += end->tv_usec - start->tv_usec ;
631
632 return diff ;
633 } /* usec_diff_timeval */
634
635
636 /* Signal handler for SIGQUIT. */
SIGALRM_handler(int signum)637 void SIGALRM_handler (int signum)
638 {
639 DPRINTF ("Autostop alarm\n") ;
640 if (!ignore_autostop){
641 DPRINTF ("Sending sigint\n") ;
642 exit(1);
643 }
644 }
645
646 static
start_autostop_timer(void)647 void start_autostop_timer(void)
648 {
649 static int firsttime = 1;
650 static struct itimerval timerval;
651 struct itimerval otimerval;
652 struct sigaction sa;
653
654 if (firsttime && stopdelay) {
655 sigemptyset (&sa.sa_mask);
656 sa.sa_flags = 0;
657
658 /* Register the handler for SIGALARM. */
659 sa.sa_handler = SIGALRM_handler;
660 sigaction (SIGALRM, &sa, 0);
661
662 timerval.it_interval.tv_sec = (long)stopdelay;
663 timerval.it_interval.tv_usec = 0;
664 timerval.it_value.tv_sec = (long)stopdelay;
665 timerval.it_value.tv_usec = 0;
666 }
667
668 if (stopdelay)
669 { ignore_autostop = 0;
670 setitimer (ITIMER_REAL, &timerval, &otimerval);
671 DPRINTF ("Watchdog started from -> %ld \n", otimerval.it_value.tv_sec) ;
672 }
673
674 } /* start_autostop_timer */
675
676 static
stop_autostop_timer(void)677 void stop_autostop_timer(void)
678 {
679 static int firsttime = 1;
680 static struct itimerval timerval;
681 struct itimerval otimerval;
682
683
684 if (firsttime && stopdelay) {
685 timerval.it_interval.tv_sec = (long)0;
686 timerval.it_interval.tv_usec = 0;
687 timerval.it_value.tv_sec = (long)0;
688 timerval.it_value.tv_usec = 0;
689 }
690
691 if (stopdelay) {
692 ignore_autostop = 1;
693 setitimer (ITIMER_REAL, &timerval, &otimerval);
694 DPRINTF ("Watchdog stopped at -> %ld \n", otimerval.it_value.tv_sec) ;
695 }
696
697 } /* stop_autostop_timer */
698